Merge VS Code 1.26.1 (#2394)

* Squash merge commits for 1.26 (#1) (#2323)

* Polish tag search as per feedback (#55269)

* Polish tag search as per feedback

* Updated regex

* Allow users to opt-out of features that send online requests in the background (#55097)

* settings sweep #54690

* Minor css tweaks to enable eoverflow elipsis in more places (#55277)

* fix an issue with titlebarheight when not scaling with zoom

* Settings descriptions update #54690

* fixes #55209

* Settings editor - many padding fixes

* More space above level 2 label

* Fixing Cannot debug npm script using Yarn #55103

* Settings editor - show ellipsis when description overflows

* Settings editor - ... fix measuring around links, relayout

* Setting descriptions

* Settings editor - fix ... for some short lines, fix select container width

* Settings editor - overlay trees so scrollable shadow is full width

* Fix #54133 - missing extension settings after reload

* Settings color token description tweak

* Settings editor - disable overflow indicator temporarily, needs to be faster

* Added command to Run the selected npm script

* fixes #54452

* fixes #54929

* fixes #55248

* prefix command with extension name

* Contribute run selected to the context menu

* node-debug@1.26.6

* Allow terminal rendererType to be swapped out at runtime

Part of #53274
Fixes #55344

* Settings editor - fix not focusing search when restoring editor
setInput must be actually async. Will be fixed naturally when we aren't using winJS promises...

* Settings editor - TOC should only expand the section with a selected item

* Bump node-debug2

* Settings editor - Tree focus outlines

* Settings editor - don't blink the scrollbar when toc selection changes
And hide TOC correctly when the editor is narrow

* Settings editor - header rows should not be selectable

* fixes #54877

* change debug assignee to isi

* Settings sweep (#54690)

* workaround for #55051

* Settings sweep (#54690)

* settings sweep

#54690

* Don't try closing tags when you type > after another >

* Describe what implementation code lens does

Fixes #55370

* fix javadoc formatter setting description

* fixes #55325

* update to officical TS version

* Settings editor - Even more padding, use semibold instead of bold

* Fix #55357 - fix TOC twistie

* fixes #55288

* explorer: refresh on di change file system provider registration

fixes #53256

* Disable push to Linux repo to test standalone publisher

* New env var to notify log level to extensions #54001

* Disable snippets in extension search (when not in suggest dropdown) (#55281)

* Disable snippits in extension search (when not in suggest dropdown)

* Add monaco input contributions

* Fix bug preventing snippetSuggestions from taking effect in sub-editors

* Latest emmet helper to fix #52366

* Fix comment updates for threads within same file

* Allow extensions to log telemetry to log files #54001

* Pull latest css grammar

* files.exclude control - use same style for "add" vs "edit"

* files.exclude control - focus/keyboard behavior

* don't show menubar too early

* files.exclude - better styling

* Place cursor at end of extensions search box on autofill (#55254)

* Place cursor at end of extensions search box on autofill

* Use position instead of selection

* fix linux build issue (empty if block)

* Settings editor - fix extension category prefixes

* Settings editor - add simple ellipsis for first line that overflows, doesn't cover case when first line does not overflow but there is more text, TODO

* File/Text search provider docs

* Fixes #52655

* Include epoch (#55008)

* Fixes #53385

* Fixes #49480

*  VS Code Insiders (Users) not opening Fixes #55353

* Better handling of the case when the extension host fails to start

* Fixes #53966

*  Remove confusing Start from wordPartLeft commands ID

* vscode-xterm@3.6.0-beta12

Fixes #55488

* Initial size is set to infinity!! Fixes #55461

* Polish embeddedEditorBackground

* configuration service misses event

* Fix #55224 - fix duplicate results in multiroot workspace from splitting the diskseach query

* Select all not working in issue reporter on mac, fixes #55424

* Disable fuzzy matching for extensions autosuggest (#55498)

* Fix clipping of extensions search border in some third party themes (#55504)

* fixes #55538

* Fix bug causing an aria alert to not be shown the third time
 (and odd numbers thereafter)

* Settings editor - work around rendering glitch with webkit-line-clamp

* Settings editor - revert earlier '...' changes

* Settings editor - move enumDescription to its own div, because it disturbs -webkit-line-clamp for some reason

* Settings editor - better overflow indicator

* Don't show existing filters in autocomplete (#55495)

* Dont show existing filters in autocomplete

* Simplify

* Settings Editor: Add aria labels for input elements Fixes: #54836 (#55543)

* fixes #55223

* Update vscode-css-languageservice to 3.0.10-next.1

* Fix #55509 - settings navigation

* Fix #55519

* Fix #55520

* FIx #55524

* Fix #55556 - include wordSeparators in all search queries, so findTextInFiles can respect isWordMatch correctly

* oss updates for endgame

* Fix unit tests

* fixes #55522

* Avoid missing manifest error from bubbling up #54757

* Settings format crawl

* Search provider - Fix FileSearchProvider to return array, not progress

* Fix #55598

* Settings editor - fix NPE rendering settings with no description

* dont render inden guides in search box (#55600)

* fixes #55454

* More settings crawl

* Another change for #55598 - maxResults applies to FileSearch and TextSearch but not FileIndex

* Fix FileSearchProvider unit tests for progress change

* fixes #55561

* Settings description update for #54690

* Update setting descriptions for online services

* Minor edits

* fixes #55513

* fixes #55451

* Fix #55612 - fix findTextInFiles cancellation

* fixes #55539

* More setting description tweaks

* Setting to disable online experiments #54354

* fixes #55507

* fixes #55515

* Show online services action only in Insiders for now

* Settings editor - change toc behavior default to 'filter'

* Settings editor - nicer filter count style during search

* Fix #55617 - search viewlet icons

* Settings editor - better styling for element count indicator

* SearchProvider - fix NPE when searching extraFileResources

* Allow extends to work without json suffix

Fixes #16905

* Remove accessability options logic entirely

Follow up on #55451

* use latest version of DAP

* fixes #55490

* fixes #55122

* fixes #52332

* Avoid assumptions about git: URIs (fixes #36236)

* relative path for descriptions

* resourece: get rid of isFile context key

fixes #48275

* Register previous ids for compatibility (#53497)

* more tuning for #48275

* no need to always re-read "files explorer"

fixes #52003

* read out active composites properly

fixes #51967

* Update link colors for hc theme to meet color contrast ratio, fixes #55651

Also updated link color for `textLinkActiveForeground` to be the same as `textLinkForeground` as it wasn't properly updated

* detect 'winpty-agent.exe'; fixes #55672

* node-debug@1.26.7

* reset counter on new label

* Settings editor - fix multiple setting links in one description

* Settings editor - color code blocks in setting descriptions, fix #55532

* Settings editor - hover color in TOC

* Settings editor - fix navigation NPE

* Settings editor - fix text control width

* Settings editor - maybe fix #55684

* Fix bug causing cursor to not move on paste

* fixes #53582

* Use ctrlCmd instead of ctrl for go down from search box

* fixes #55264

* fixes #55456

* filter for spcaes before triggering search (#55611)

* Fix #55698 - don't lose filtered TOC counts when refreshing TOC

* fixes #55421

* fixes #28979

* fixes #55576

* only add check for updates to windows/linux help

* readonly files: append decoration to label

fixes #53022

* debug: do not show toolbar while initialising

fixes #55026

* Opening launch.json should not activate debug extensions

fixes #55029

* fixes #55435

* fixes #55434

* fixes #55439

* trigger menu only on altkey up

* Fix #50555 - fix settings editor memory leak

* Fix #55712 - no need to focus 'a' anymore when restoring control focus after tree render

* fixes #55335

* proper fix for readonly model

fixes #53022

* improve FoldingRangeKind spec (for #55686)

* Use class with static fields (fixes #55494)

* Fixes #53671

* fixes #54630

* [html] should disable ionic suggestions by default. Currently forces deprecated Ionic v1 suggestions in .html files while typing. Fixes #53324

* cleanup deps

* debug issues back to andre

* update electron for smoketest

* Fix #55757 - prevent settings tabs from overflowing

* Fix #53897 - revert setting menu defaults to old editor

* Add enum descriptions to `typescript.preferences.importModuleSpecifier`

* Fix #55767 - leaking style elements from settings editor

* Fix #55521 - prevent flashing when clicking in exclude control

* Update Git modified color for contrast ratio, fixes #53140

* Revert "Merge branch 'master' of github.com:Microsoft/vscode"

This reverts commit bf46b6bfbae0cab99c2863e1244a916181fa9fbc, reversing
changes made to e275a424483dfb4ed33b428c97d5e2c441d6b917.

* Revert "Revert "Merge branch 'master' of github.com:Microsoft/vscode""

This reverts commit 53949d963f39e40757557c6526332354a31d9154.

* don't ask to install an incomplete menu

* Fix NPE in terminal AccessibilityManager

Fixes #55744

* don't display fallback menu unless we've closed the last window

* fixes #55547

* Fix smoke tests for extension search box

* Update OSSREADME.json for Electron 2.0.5

* Update distro

Includes Chromium license changes

* fix #55455

* fix #55865

* fixes #55893

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation (#55805)

* Fix bug causing workspace recommendations to go away upon ignoring a recommendation

* ONly show on @recommended or @recommended:workspace

* Make more consistant

* Fix #55911

* Understand json activity (#55926)

* Understand json file activity

* Refactoring

* adding composer.json

* Distro update for experiments

* use terminal.processId for auto-attach; fixes #55918

* Reject invalid URI with vscode.openFolder (for #55891)

* improve win32 setup system vs user detection

fixes #55840

fixes #55840

delay winreg import

related to #55840

show notification earlier

related to #55840

fix #55840

update inno setup message

related to #55840

* Fix #55593 - this code only operates on local paths, so use fsPath and Uri.file instead

* Bring back the old menu due to electron 2.0 issues (#55913)

* add the old menu back for native menus

* make menu labels match

* `vscode.openFolder`: treat missing URI schema gracefully (for #55891)

* delay EH reattach; fixes #55955

* Mark all json files under appSettingsHome as settings

* Use localized strings for telemetry opt-out

* Exception when saving file editor opened from remote file provider (fixes #55051)

* Remove terminal menu from stable

Fixes 56003

* VSCode Insiders crashes on open with TypeError: Cannot read property 'lastIndexOf' of undefined. Fixes #54933

* improve fix for #55891

* fix #55916

* Improve #55891

* increase EH debugging restart delay; fixes #55955

* Revert "Don't include non-resource entries in history quick pick"

This reverts commit 37209a838e9f7e9abe6dc53ed73cdf1e03b72060.

* Diff editor: horizontal scrollbar height is smaller (fixes #56062)

* improve openFolder uri fix (correctly treat backslashes)

* fixes #56116
repair ipc for native menubar keybindings

* Fix #56240 - Open the JSON settings editor instead of the UI editor

* Fix #55536

* uriDisplay: if no formatter is registered fall back to getPathlabel

fixes #56104

* VSCode hangs when opening python file. Fixes #56377

* VS Code Hangs When Opening Specific PowerShell File. Fixes #56430

* Fix #56433 - search extraFileResources even when no folders open

* Workaround #55649

* Fix in master #56371

* Fix tests #56371

* Fix in master #56317

* increase version to 1.26.1

* Fixes #56387: Handle SIGPIPE in extension host

* fixes #56185

* Fix merge issues (part 1)

* Fix build breaks (part 1)

* Build breaks (part 2)

* Build breaks (part 3)

* More build breaks (part 4)

* Fix build breaks (part 5)

* WIP

* Fix menus

* Render query result and message panels (#2363)

* Put back query editor hot exit changes

* Fix grid changes that broke profiler (#2365)

* Update APIs for saving query editor state

* Fix restore view state for profiler and edit data

* Updating custom default themes to support 4.5:1 contrast ratio

* Test updates

* Fix Extension Manager and Windows Setup

* Update license headers

* Add appveyor and travis files back

* Fix hidden modal dropdown issue
This commit is contained in:
Karl Burtram
2018-09-04 14:55:00 -07:00
committed by GitHub
parent 3763278366
commit 81329fa7fa
2638 changed files with 118456 additions and 64012 deletions

View File

@@ -22,6 +22,7 @@ import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerColor } from 'vs/platform/theme/common/colorRegistry';
import { TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from 'vs/editor/common/model';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hc: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.'));
@@ -34,7 +35,8 @@ class JumpToBracketAction extends EditorAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_BACKSLASH,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -116,7 +118,13 @@ export class BracketMatchingController extends Disposable implements editorCommo
this._updateBracketsSoon.schedule();
}));
this._register(editor.onDidChangeModel((e) => { this._decorations = []; this._updateBracketsSoon.schedule(); }));
this._register(editor.onDidChangeModelContent((e) => {
this._updateBracketsSoon.schedule();
}));
this._register(editor.onDidChangeModel((e) => {
this._decorations = [];
this._updateBracketsSoon.schedule();
}));
this._register(editor.onDidChangeModelLanguageConfiguration((e) => {
this._lastBracketsData = [];
this._updateBracketsSoon.schedule();

View File

@@ -23,10 +23,10 @@ class MoveCaretAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
var commands: ICommand[] = [];
var selections = editor.getSelections();
let commands: ICommand[] = [];
let selections = editor.getSelections();
for (var i = 0; i < selections.length; i++) {
for (let i = 0; i < selections.length; i++) {
commands.push(new MoveCaretCommand(selections[i], this.left));
}

View File

@@ -26,7 +26,7 @@ export class MoveCaretCommand implements ICommand {
}
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
var s = this._selection;
let s = this._selection;
this._selectionId = builder.trackSelection(s);
if (s.startLineNumber !== s.endLineNumber) {
return;
@@ -37,12 +37,12 @@ export class MoveCaretCommand implements ICommand {
return;
}
var lineNumber = s.selectionStartLineNumber;
var lineContent = model.getLineContent(lineNumber);
let lineNumber = s.selectionStartLineNumber;
let lineContent = model.getLineContent(lineNumber);
var left;
var middle;
var right;
let left: string;
let middle: string;
let right: string;
if (this._isMovingLeft) {
left = lineContent.substring(0, s.startColumn - 2);
@@ -54,7 +54,7 @@ export class MoveCaretCommand implements ICommand {
right = lineContent.substring(s.endColumn);
}
var newLineContent = left + middle + right;
let newLineContent = left + middle + right;
builder.addEditOperation(new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber)), null);
builder.addEditOperation(new Range(lineNumber, 1, lineNumber, 1), newLineContent);
@@ -65,7 +65,7 @@ export class MoveCaretCommand implements ICommand {
}
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
var result = helper.getTrackedSelection(this._selectionId);
let result = helper.getTrackedSelection(this._selectionId);
if (this._moved) {
result = result.setStartPosition(result.startLineNumber, this._cutStartIndex);
result = result.setEndPosition(result.startLineNumber, this._cutEndIndex);

View File

@@ -15,6 +15,7 @@ import { registerEditorAction, EditorAction, ServicesAccessor } from 'vs/editor/
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel } from 'vs/editor/common/model';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
class TransposeLettersAction extends EditorAction {
@@ -67,7 +68,8 @@ class TransposeLettersAction extends EditorAction {
primary: 0,
mac: {
primary: KeyMod.WinCtrl | KeyCode.KEY_T
}
},
weight: KeybindingWeight.EditorContrib
}
});
}

View File

@@ -16,6 +16,8 @@ import { registerEditorAction, IActionOptions, EditorAction, ICommandKeybindings
import { CopyOptions } from 'vs/editor/browser/controller/textAreaInput';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MenuId } from 'vs/platform/actions/common/actions';
const CLIPBOARD_CONTEXT_MENU_GROUP = '9_cutcopypaste';
@@ -42,7 +44,7 @@ abstract class ExecCommandAction extends EditorAction {
public runCommand(accessor: ServicesAccessor, args: any): void {
let focusedEditor = accessor.get(ICodeEditorService).getFocusedCodeEditor();
// Only if editor text focus (i.e. not if editor has widget focus).
if (focusedEditor && focusedEditor.isFocused()) {
if (focusedEditor && focusedEditor.hasTextFocus()) {
focusedEditor.trigger('keyboard', this.id, args);
return;
}
@@ -62,7 +64,8 @@ class ExecCommandCutAction extends ExecCommandAction {
let kbOpts: ICommandKeybindingsOptions = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_X,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] }
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_X, secondary: [KeyMod.Shift | KeyCode.Delete] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind cut keybindings in the browser,
// since browsers do that for us and it avoids security prompts
@@ -78,6 +81,12 @@ class ExecCommandCutAction extends ExecCommandAction {
menuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 1
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCut', comment: ['&& denotes a mnemonic'] }, "Cu&&t"),
order: 1
}
});
}
@@ -99,7 +108,8 @@ class ExecCommandCopyAction extends ExecCommandAction {
let kbOpts: ICommandKeybindingsOptions = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_C,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_C, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind copy keybindings in the browser,
// since browsers do that for us and it avoids security prompts
@@ -116,6 +126,12 @@ class ExecCommandCopyAction extends ExecCommandAction {
menuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 2
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miCopy', comment: ['&& denotes a mnemonic'] }, "&&Copy"),
order: 2
}
});
}
@@ -137,7 +153,8 @@ class ExecCommandPasteAction extends ExecCommandAction {
let kbOpts: ICommandKeybindingsOptions = {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_V,
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] }
win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.Shift | KeyCode.Insert] },
weight: KeybindingWeight.EditorContrib
};
// Do not bind paste keybindings in the browser,
// since browsers do that for us and it avoids security prompts
@@ -154,6 +171,12 @@ class ExecCommandPasteAction extends ExecCommandAction {
menuOpts: {
group: CLIPBOARD_CONTEXT_MENU_GROUP,
order: 3
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '2_ccp',
title: nls.localize({ key: 'miPaste', comment: ['&& denotes a mnemonic'] }, "&&Paste"),
order: 3
}
});
}
@@ -169,7 +192,8 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: null
primary: null,
weight: KeybindingWeight.EditorContrib
}
});
}

View File

@@ -3,34 +3,42 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isFalsyOrEmpty, mergeSort, flatten } from 'vs/base/common/arrays';
import { flatten, isFalsyOrEmpty, mergeSort } from 'vs/base/common/arrays';
import { asWinJsPromise } from 'vs/base/common/async';
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import { CancellationToken } from 'vs/base/common/cancellation';
import { illegalArgument, isPromiseCanceledError, onUnexpectedExternalError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
import { CodeAction, CodeActionContext, CodeActionProviderRegistry, CodeActionTrigger as CodeActionTriggerKind } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CodeActionFilter, CodeActionKind } from './codeActionTrigger';
import { CodeActionFilter, CodeActionKind, CodeActionTrigger } from './codeActionTrigger';
export function getCodeActions(model: ITextModel, range: Range, filter?: CodeActionFilter): TPromise<CodeAction[]> {
const codeActionContext = { only: filter && filter.kind ? filter.kind.value : undefined };
export function getCodeActions(model: ITextModel, rangeOrSelection: Range | Selection, trigger?: CodeActionTrigger, token: CancellationToken = CancellationToken.None): Promise<CodeAction[]> {
const codeActionContext: CodeActionContext = {
only: trigger && trigger.filter && trigger.filter.kind ? trigger.filter.kind.value : undefined,
trigger: trigger && trigger.type === 'manual' ? CodeActionTriggerKind.Manual : CodeActionTriggerKind.Automatic
};
const promises = CodeActionProviderRegistry.all(model).map(support => {
return asWinJsPromise(token => support.provideCodeActions(model, range, codeActionContext, token)).then(providedCodeActions => {
return asWinJsPromise(token => support.provideCodeActions(model, rangeOrSelection, codeActionContext, token)).then(providedCodeActions => {
if (!Array.isArray(providedCodeActions)) {
return [];
}
return providedCodeActions.filter(action => isValidAction(filter, action));
return providedCodeActions.filter(action => isValidAction(trigger && trigger.filter, action));
}, (err): CodeAction[] => {
if (isPromiseCanceledError(err)) {
throw err;
}
onUnexpectedExternalError(err);
return [];
});
});
return TPromise.join(promises)
return Promise.all(promises)
.then(flatten)
.then(allCodeActions => mergeSort(allCodeActions, codeActionsComparator));
}
@@ -80,5 +88,5 @@ registerLanguageCommand('_executeCodeActionProvider', function (accessor, args)
throw illegalArgument();
}
return getCodeActions(model, model.validateRange(range));
return getCodeActions(model, model.validateRange(range), { type: 'manual', filter: { includeSourceActions: true } });
});

View File

@@ -3,30 +3,30 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { CancelablePromise } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { BulkEdit } from 'vs/editor/browser/services/bulkEdit';
import { IBulkEditService } from 'vs/editor/browser/services/bulkEditService';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CodeAction } from 'vs/editor/common/modes';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { MessageController } from 'vs/editor/contrib/message/messageController';
import * as nls from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IFileService } from 'vs/platform/files/common/files';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { CodeActionModel, CodeActionsComputeEvent, SUPPORTED_CODE_ACTIONS } from './codeActionModel';
import { CodeActionAutoApply, CodeActionFilter, CodeActionKind } from './codeActionTrigger';
import { CodeActionContextMenu } from './codeActionWidget';
import { LightBulbWidget } from './lightBulbWidget';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
function contextKeyForSupportedActions(kind: CodeActionKind) {
return ContextKeyExpr.regex(
@@ -48,17 +48,19 @@ export class QuickFixController implements IEditorContribution {
private _lightBulbWidget: LightBulbWidget;
private _disposables: IDisposable[] = [];
private _activeRequest: CancelablePromise<CodeAction[]> | undefined;
constructor(editor: ICodeEditor,
@IMarkerService markerService: IMarkerService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService private readonly _commandService: ICommandService,
@IProgressService progressService: IProgressService,
@IContextMenuService contextMenuService: IContextMenuService,
@ICommandService private readonly _commandService: ICommandService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@ITextModelService private readonly _textModelService: ITextModelService,
@optional(IFileService) private _fileService: IFileService
@IBulkEditService private readonly _bulkEditService: IBulkEditService,
) {
this._editor = editor;
this._model = new CodeActionModel(this._editor, markerService, contextKeyService);
this._model = new CodeActionModel(this._editor, markerService, contextKeyService, progressService);
this._codeActionContextMenu = new CodeActionContextMenu(editor, contextMenuService, action => this._onApplyCodeAction(action));
this._lightBulbWidget = new LightBulbWidget(editor);
@@ -78,7 +80,16 @@ export class QuickFixController implements IEditorContribution {
}
private _onCodeActionsEvent(e: CodeActionsComputeEvent): void {
if (e && e.trigger.filter && e.trigger.filter.kind) {
if (this._activeRequest) {
this._activeRequest.cancel();
this._activeRequest = undefined;
}
if (e && e.actions) {
this._activeRequest = e.actions;
}
if (e && e.actions && e.trigger.filter && e.trigger.filter.kind) {
// Triggered for specific scope
// Apply if we only have one action or requested autoApply, otherwise show menu
e.actions.then(fixes => {
@@ -112,10 +123,12 @@ export class QuickFixController implements IEditorContribution {
}
private _handleLightBulbSelect(coords: { x: number, y: number }): void {
this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords);
if (this._lightBulbWidget.model.actions) {
this._codeActionContextMenu.show(this._lightBulbWidget.model.actions, coords);
}
}
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): TPromise<CodeAction[] | undefined> {
public triggerFromEditorSelection(filter?: CodeActionFilter, autoApply?: CodeActionAutoApply): Thenable<CodeAction[] | undefined> {
return this._model.trigger({ type: 'manual', filter, autoApply });
}
@@ -130,20 +143,19 @@ export class QuickFixController implements IEditorContribution {
this._lightBulbWidget.title = title;
}
private async _onApplyCodeAction(action: CodeAction): TPromise<void> {
await applyCodeAction(action, this._textModelService, this._fileService, this._commandService, this._editor);
private _onApplyCodeAction(action: CodeAction): TPromise<void> {
return TPromise.wrap(applyCodeAction(action, this._bulkEditService, this._commandService, this._editor));
}
}
export async function applyCodeAction(
action: CodeAction,
textModelService: ITextModelService,
fileService: IFileService,
bulkEditService: IBulkEditService,
commandService: ICommandService,
editor: ICodeEditor,
) {
editor?: ICodeEditor,
): Promise<void> {
if (action.edit) {
await BulkEdit.perform(action.edit.edits, textModelService, fileService, editor);
await bulkEditService.apply(action.edit, { editor });
}
if (action.command) {
await commandService.executeCommand(action.command.id, ...action.command.arguments);
@@ -181,12 +193,13 @@ export class QuickFixAction extends EditorAction {
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCodeActionsProvider),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.US_DOT
primary: KeyMod.CtrlCmd | KeyCode.US_DOT,
weight: KeybindingWeight.EditorContrib
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"));
}
}
@@ -239,7 +252,7 @@ export class CodeActionCommand extends EditorCommand {
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) {
public runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor, userArg: any) {
const args = CodeActionCommandArgs.fromUser(userArg);
return showCodeActionsForEditorSelection(editor, nls.localize('editor.action.quickFix.noneMessage', "No code actions available"), { kind: args.kind, includeSourceActions: true }, args.apply);
}
@@ -261,7 +274,8 @@ export class RefactorAction extends EditorAction {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R,
mac: {
primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R
}
},
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
group: '1_modification',
@@ -273,7 +287,7 @@ export class RefactorAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
return showCodeActionsForEditorSelection(editor,
nls.localize('editor.action.refactor.noneMessage', "No refactorings available"),
{ kind: CodeActionKind.Refactor },
@@ -302,7 +316,7 @@ export class SourceAction extends EditorAction {
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
return showCodeActionsForEditorSelection(editor,
nls.localize('editor.action.source.noneMessage', "No source actions available"),
{ kind: CodeActionKind.Source, includeSourceActions: true },
@@ -324,12 +338,13 @@ export class OrganizeImportsAction extends EditorAction {
contextKeyForSupportedActions(CodeActionKind.SourceOrganizeImports)),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_O,
weight: KeybindingWeight.EditorContrib
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
public run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
return showCodeActionsForEditorSelection(editor,
nls.localize('editor.action.organize.noneMessage', "No organize imports action available"),
{ kind: CodeActionKind.SourceOrganizeImports, includeSourceActions: true },

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { SourceAction, QuickFixController, QuickFixAction, CodeActionCommand, RefactorAction, OrganizeImportsAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionCommand, OrganizeImportsAction, QuickFixAction, QuickFixController, RefactorAction, SourceAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
registerEditorContribution(QuickFixController);

View File

@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Emitter, Event, debounceEvent } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { debounceEvent, Emitter, Event } from 'vs/base/common/event';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -14,6 +15,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { CodeAction, CodeActionProviderRegistry } from 'vs/editor/common/modes';
import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IMarkerService } from 'vs/platform/markers/common/markers';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { getCodeActions } from './codeAction';
import { CodeActionTrigger } from './codeActionTrigger';
@@ -25,9 +27,10 @@ export class CodeActionOracle {
constructor(
private _editor: ICodeEditor,
private _markerService: IMarkerService,
private readonly _markerService: IMarkerService,
private _signalChange: (e: CodeActionsComputeEvent) => any,
delay: number = 250
delay: number = 250,
private readonly _progressService?: IProgressService,
) {
this._disposables.push(
debounceEvent(this._markerService.onMarkerChanged, (last, cur) => last ? last.concat(cur) : cur, delay / 2)(e => this._onMarkerChanges(e)),
@@ -40,11 +43,8 @@ export class CodeActionOracle {
}
trigger(trigger: CodeActionTrigger) {
let rangeOrSelection = this._getRangeOfMarker() || this._getRangeOfSelectionUnlessWhitespaceEnclosed();
if (!rangeOrSelection && trigger.type === 'manual') {
rangeOrSelection = this._editor.getSelection();
}
return this._createEventAndSignalChange(trigger, rangeOrSelection);
const selection = this._getRangeOfSelectionUnlessWhitespaceEnclosed(trigger);
return this._createEventAndSignalChange(trigger, selection);
}
private _onMarkerChanges(resources: URI[]): void {
@@ -61,8 +61,7 @@ export class CodeActionOracle {
this.trigger({ type: 'auto' });
}
private _getRangeOfMarker(): Range {
const selection = this._editor.getSelection();
private _getRangeOfMarker(selection: Selection): Range {
const model = this._editor.getModel();
for (const marker of this._markerService.read({ resource: model.uri })) {
if (Range.intersectRanges(marker, selection)) {
@@ -72,10 +71,10 @@ export class CodeActionOracle {
return undefined;
}
private _getRangeOfSelectionUnlessWhitespaceEnclosed(): Selection {
private _getRangeOfSelectionUnlessWhitespaceEnclosed(trigger: CodeActionTrigger): Selection | undefined {
const model = this._editor.getModel();
const selection = this._editor.getSelection();
if (selection.isEmpty()) {
if (selection.isEmpty() && !(trigger.filter && trigger.filter.includeSourceActions)) {
const { lineNumber, column } = selection.getPosition();
const line = model.getLineContent(lineNumber);
if (line.length === 0) {
@@ -101,26 +100,29 @@ export class CodeActionOracle {
return selection;
}
private _createEventAndSignalChange(trigger: CodeActionTrigger, rangeOrSelection: Range | Selection): TPromise<CodeAction[] | undefined> {
if (!rangeOrSelection) {
private _createEventAndSignalChange(trigger: CodeActionTrigger, selection: Selection | undefined): Thenable<CodeAction[] | undefined> {
if (!selection) {
// cancel
this._signalChange({
trigger,
range: undefined,
rangeOrSelection: undefined,
position: undefined,
actions: undefined,
});
return TPromise.as(undefined);
} else {
// actual
const model = this._editor.getModel();
const range = model.validateRange(rangeOrSelection);
const position = rangeOrSelection instanceof Selection ? rangeOrSelection.getPosition() : rangeOrSelection.getStartPosition();
const actions = getCodeActions(model, range, trigger && trigger.filter);
const markerRange = this._getRangeOfMarker(selection);
const position = markerRange ? markerRange.getStartPosition() : selection.getStartPosition();
const actions = createCancelablePromise(token => getCodeActions(model, selection, trigger, token));
if (this._progressService && trigger.type === 'manual') {
this._progressService.showWhile(TPromise.wrap(actions), 250);
}
this._signalChange({
trigger,
range,
rangeOrSelection: selection,
position,
actions
});
@@ -131,9 +133,9 @@ export class CodeActionOracle {
export interface CodeActionsComputeEvent {
trigger: CodeActionTrigger;
range: Range;
rangeOrSelection: Range | Selection;
position: Position;
actions: TPromise<CodeAction[]>;
actions: CancelablePromise<CodeAction[]>;
}
export class CodeActionModel {
@@ -145,7 +147,7 @@ export class CodeActionModel {
private _disposables: IDisposable[] = [];
private readonly _supportedCodeActions: IContextKey<string>;
constructor(editor: ICodeEditor, markerService: IMarkerService, contextKeyService: IContextKeyService) {
constructor(editor: ICodeEditor, markerService: IMarkerService, contextKeyService: IContextKeyService, private readonly _progressService: IProgressService) {
this._editor = editor;
this._markerService = markerService;
@@ -188,14 +190,14 @@ export class CodeActionModel {
this._supportedCodeActions.set(supportedActions.join(' '));
this._codeActionOracle = new CodeActionOracle(this._editor, this._markerService, p => this._onDidChangeFixes.fire(p));
this._codeActionOracle = new CodeActionOracle(this._editor, this._markerService, p => this._onDidChangeFixes.fire(p), undefined, this._progressService);
this._codeActionOracle.trigger({ type: 'auto' });
} else {
this._supportedCodeActions.reset();
}
}
trigger(trigger: CodeActionTrigger): TPromise<CodeAction[] | undefined> {
trigger(trigger: CodeActionTrigger): Thenable<CodeAction[] | undefined> {
if (this._codeActionOracle) {
return this._codeActionOracle.trigger(trigger);
}

View File

@@ -9,6 +9,7 @@ export class CodeActionKind {
private static readonly sep = '.';
public static readonly Empty = new CodeActionKind('');
public static readonly QuickFix = new CodeActionKind('quickfix');
public static readonly Refactor = new CodeActionKind('refactor');
public static readonly Source = new CodeActionKind('source');
public static readonly SourceOrganizeImports = new CodeActionKind('source.organizeImports');

View File

@@ -28,7 +28,7 @@ export class CodeActionContextMenu {
private readonly _onApplyCodeAction: (action: CodeAction) => TPromise<any>
) { }
show(fixes: TPromise<CodeAction[]>, at: { x: number; y: number } | Position) {
show(fixes: Thenable<CodeAction[]>, at: { x: number; y: number } | Position) {
const actions = fixes.then(value => {
return value.map(action => {
@@ -53,8 +53,11 @@ export class CodeActionContextMenu {
}
return at;
},
getActions: () => actions,
onHide: () => { this._visible = false; },
getActions: () => TPromise.wrap(actions),
onHide: () => {
this._visible = false;
this._editor.focus();
},
autoSelectFirstItem: true
});
}

View File

@@ -7,10 +7,11 @@ import * as dom from 'vs/base/browser/dom';
import { GlobalMouseMoveMonitor, IStandardMouseMoveEventData, standardMouseMoveMerger } from 'vs/base/browser/globalMouseMoveMonitor';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { Emitter, Event } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import 'vs/css!./lightBulbWidget';
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { CodeActionsComputeEvent } from './codeActionModel';
export class LightBulbWidget implements IDisposable, IContentWidget {
@@ -52,7 +53,7 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
const { lineHeight } = this._editor.getConfiguration();
let pad = Math.floor(lineHeight / 3);
if (this._position.position.lineNumber < this._model.position.lineNumber) {
if (this._position && this._position.position.lineNumber < this._model.position.lineNumber) {
pad += lineHeight;
}
@@ -114,13 +115,18 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
const { token } = this._futureFixes;
this._model = value;
this._model.actions.done(fixes => {
const selection = this._model.rangeOrSelection;
this._model.actions.then(fixes => {
if (!token.isCancellationRequested && fixes && fixes.length > 0) {
this._show();
if (selection.isEmpty() && fixes.every(fix => fix.kind && CodeActionKind.Refactor.contains(fix.kind))) {
this.hide();
} else {
this._show();
}
} else {
this.hide();
}
}, err => {
}).catch(err => {
this.hide();
});
}
@@ -144,6 +150,10 @@ export class LightBulbWidget implements IDisposable, IContentWidget {
}
const { lineNumber } = this._model.position;
const model = this._editor.getModel();
if (!model) {
return;
}
const tabSize = model.getOptions().tabSize;
const lineContent = model.getLineContent(lineNumber);
const indent = TextModel.computeIndentLevel(lineContent, tabSize);

View File

@@ -5,14 +5,14 @@
'use strict';
import * as assert from 'assert';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeActionProviderRegistry, LanguageIdentifier, CodeActionProvider, Command, WorkspaceEdit, ResourceTextEdit, CodeAction, CodeActionContext } from 'vs/editor/common/modes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeAction, CodeActionContext, CodeActionProvider, CodeActionProviderRegistry, Command, LanguageIdentifier, ResourceTextEdit, WorkspaceEdit } from 'vs/editor/common/modes';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/codeActionTrigger';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
suite('CodeAction', () => {
@@ -56,7 +56,7 @@ suite('CodeAction', () => {
},
spelling: {
bcd: {
diagnostics: [],
diagnostics: <IMarkerData[]>[],
edit: new class implements WorkspaceEdit {
edits: ResourceTextEdit[];
},
@@ -66,13 +66,13 @@ suite('CodeAction', () => {
tsLint: {
abc: {
$ident: 57,
arguments: [],
arguments: <IMarkerData[]>[],
id: '_internal_command_delegation',
title: 'abc'
},
bcd: {
$ident: 47,
arguments: [],
arguments: <IMarkerData[]>[],
id: '_internal_command_delegation',
title: 'bcd'
}
@@ -136,20 +136,20 @@ suite('CodeAction', () => {
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
{
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { kind: new CodeActionKind('a') });
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } });
assert.equal(actions.length, 2);
assert.strictEqual(actions[0].title, 'a');
assert.strictEqual(actions[1].title, 'a.b');
}
{
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { kind: new CodeActionKind('a.b') });
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b') } });
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a.b');
}
{
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { kind: new CodeActionKind('a.b.c') });
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a.b.c') } });
assert.equal(actions.length, 0);
}
});
@@ -165,7 +165,7 @@ suite('CodeAction', () => {
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { kind: new CodeActionKind('a') });
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: new CodeActionKind('a') } });
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
});
@@ -183,13 +183,13 @@ suite('CodeAction', () => {
disposables.push(CodeActionProviderRegistry.register('fooLang', provider));
{
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), {});
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto' });
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'b');
}
{
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { kind: CodeActionKind.Source, includeSourceActions: true });
const actions = await getCodeActions(model, new Range(1, 1, 2, 1), { type: 'auto', filter: { kind: CodeActionKind.Source, includeSourceActions: true } });
assert.equal(actions.length, 1);
assert.strictEqual(actions[0].title, 'a');
}

View File

@@ -2,20 +2,23 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
import { CodeActionProviderRegistry, LanguageIdentifier } from 'vs/editor/common/modes';
import { CodeActionOracle } from 'vs/editor/contrib/codeAction/codeActionModel';
import { createTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { MarkerService } from 'vs/platform/markers/common/markerService';
import { CodeActionOracle } from 'vs/editor/contrib/codeAction/codeActionModel';
import { CodeActionProviderRegistry, LanguageIdentifier } from 'vs/editor/common/modes';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Range } from 'vs/editor/common/core/range';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
const testProvider = {
provideCodeActions() {
return [{ id: 'test-command', title: 'test', arguments: [] }];
}
};
suite('CodeAction', () => {
const languageIdentifier = new LanguageIdentifier('foo-lang', 3);
@@ -23,28 +26,26 @@ suite('CodeAction', () => {
let model: TextModel;
let markerService: MarkerService;
let editor: ICodeEditor;
let reg: IDisposable;
let disposables: IDisposable[];
setup(() => {
reg = CodeActionProviderRegistry.register(languageIdentifier.language, {
provideCodeActions() {
return [{ id: 'test-command', title: 'test', arguments: [] }];
}
});
disposables = [];
markerService = new MarkerService();
model = TextModel.createFromString('foobar foo bar\nfarboo far boo', undefined, languageIdentifier, uri);
editor = createTestCodeEditor(model);
editor = createTestCodeEditor({ model: model });
editor.setPosition({ lineNumber: 1, column: 1 });
});
teardown(() => {
reg.dispose();
dispose(disposables);
editor.dispose();
model.dispose();
markerService.dispose();
});
test('Orcale -> marker added', done => {
const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider);
disposables.push(reg);
const oracle = new CodeActionOracle(editor, markerService, e => {
assert.equal(e.trigger.type, 'auto');
@@ -69,6 +70,8 @@ suite('CodeAction', () => {
});
test('Orcale -> position changed', () => {
const reg = CodeActionProviderRegistry.register(languageIdentifier.language, testProvider);
disposables.push(reg);
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
@@ -96,55 +99,13 @@ suite('CodeAction', () => {
});
});
test('Oracle -> marker wins over selection', () => {
let range: Range;
let reg = CodeActionProviderRegistry.register(languageIdentifier.language, {
provideCodeActions(doc, _range) {
range = _range;
return [];
}
});
markerService.changeOne('fake', uri, [{
startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6,
message: 'error',
severity: 1,
code: '',
source: ''
}]);
let fixes: TPromise<any>[] = [];
let oracle = new CodeActionOracle(editor, markerService, e => {
fixes.push(e.actions);
}, 10);
editor.setSelection({ startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 13 });
return TPromise.join<any>([TPromise.timeout(20)].concat(fixes)).then(_ => {
// -> marker wins
assert.deepEqual(range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6 });
// 'auto' triggered, non-empty selection BUT within a marker
editor.setSelection({ startLineNumber: 1, startColumn: 2, endLineNumber: 1, endColumn: 4 });
return TPromise.join([TPromise.timeout(20)].concat(fixes)).then(_ => {
reg.dispose();
oracle.dispose();
// assert marker
assert.deepEqual(range, { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 6 });
});
});
});
test('Lightbulb is in the wrong place, #29933', async function () {
let reg = CodeActionProviderRegistry.register(languageIdentifier.language, {
provideCodeActions(doc, _range) {
const reg = CodeActionProviderRegistry.register(languageIdentifier.language, {
provideCodeActions(_doc, _range) {
return [];
}
});
disposables.push(reg);
editor.getModel().setValue('// @ts-check\n2\ncon\n');
@@ -161,7 +122,11 @@ suite('CodeAction', () => {
let oracle = new CodeActionOracle(editor, markerService, e => {
assert.equal(e.trigger.type, 'auto');
assert.deepEqual(e.range, { startLineNumber: 3, startColumn: 1, endLineNumber: 3, endColumn: 4 });
const selection = <Selection>e.rangeOrSelection;
assert.deepEqual(selection.selectionStartLineNumber, 1);
assert.deepEqual(selection.selectionStartColumn, 1);
assert.deepEqual(selection.endLineNumber, 4);
assert.deepEqual(selection.endColumn, 1);
assert.deepEqual(e.position, { lineNumber: 3, column: 1 });
oracle.dispose();
@@ -186,9 +151,5 @@ suite('CodeAction', () => {
// oracle.trigger('manual');
// });
reg.dispose();
});
});

View File

@@ -8,12 +8,10 @@
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import { mergeSort } from 'vs/base/common/arrays';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITextModel } from 'vs/editor/common/model';
import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { CodeLensProviderRegistry, CodeLensProvider, ICodeLensSymbol } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface ICodeLensData {
@@ -21,20 +19,20 @@ export interface ICodeLensData {
provider: CodeLensProvider;
}
export function getCodeLensData(model: ITextModel): TPromise<ICodeLensData[]> {
export function getCodeLensData(model: ITextModel, token: CancellationToken): Promise<ICodeLensData[]> {
const symbols: ICodeLensData[] = [];
const provider = CodeLensProviderRegistry.ordered(model);
const promises = provider.map(provider => asWinJsPromise(token => provider.provideCodeLenses(model, token)).then(result => {
const promises = provider.map(provider => Promise.resolve(provider.provideCodeLenses(model, token)).then(result => {
if (Array.isArray(result)) {
for (let symbol of result) {
symbols.push({ symbol, provider });
}
}
}, onUnexpectedExternalError));
}).catch(onUnexpectedExternalError));
return TPromise.join(promises).then(() => {
return Promise.all(promises).then(() => {
return mergeSort(symbols, (a, b) => {
// sort by lineNumber, provider-rank, and column
@@ -70,7 +68,7 @@ registerLanguageCommand('_executeCodeLensProvider', function (accessor, args) {
}
const result: ICodeLensSymbol[] = [];
return getCodeLensData(model).then(value => {
return getCodeLensData(model, CancellationToken.None).then(value => {
let resolve: Thenable<any>[] = [];

View File

@@ -5,21 +5,20 @@
'use strict';
import { RunOnceScheduler, asWinJsPromise } from 'vs/base/common/async';
import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { ICommandService } from 'vs/platform/commands/common/commands';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeLensData, getCodeLensData } from './codelens';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model';
import { CodeLensProviderRegistry, ICodeLensSymbol } from 'vs/editor/common/modes';
import { CodeLens, CodeLensHelper } from 'vs/editor/contrib/codelens/codelensWidget';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { StableEditorScrollState } from 'vs/editor/browser/core/editorState';
import { getCodeLensData, ICodeLensData } from './codelens';
export class CodeLensContribution implements editorCommon.IEditorContribution {
@@ -30,9 +29,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
private _globalToDispose: IDisposable[];
private _localToDispose: IDisposable[];
private _lenses: CodeLens[];
private _currentFindCodeLensSymbolsPromise: TPromise<ICodeLensData[]>;
private _currentFindCodeLensSymbolsPromise: CancelablePromise<ICodeLensData[]>;
private _modelChangeCounter: number;
private _currentFindOccPromise: TPromise<any>;
private _currentResolveCodeLensSymbolsPromise: CancelablePromise<any>;
private _detectVisibleLenses: RunOnceScheduler;
constructor(
@@ -72,9 +71,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._currentFindCodeLensSymbolsPromise = null;
this._modelChangeCounter++;
}
if (this._currentFindOccPromise) {
this._currentFindOccPromise.cancel();
this._currentFindOccPromise = null;
if (this._currentResolveCodeLensSymbolsPromise) {
this._currentResolveCodeLensSymbolsPromise.cancel();
this._currentResolveCodeLensSymbolsPromise = null;
}
this._localToDispose = dispose(this._localToDispose);
}
@@ -117,7 +116,7 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._currentFindCodeLensSymbolsPromise.cancel();
}
this._currentFindCodeLensSymbolsPromise = getCodeLensData(model);
this._currentFindCodeLensSymbolsPromise = createCancelablePromise(token => getCodeLensData(model, token));
this._currentFindCodeLensSymbolsPromise.then((result) => {
if (counterValue === this._modelChangeCounter) { // only the last one wins
@@ -168,22 +167,20 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
this._localToDispose.push(this._editor.onDidLayoutChange(e => {
this._detectVisibleLenses.schedule();
}));
this._localToDispose.push({
dispose: () => {
if (this._editor.getModel()) {
const scrollState = StableEditorScrollState.capture(this._editor);
this._editor.changeDecorations((changeAccessor) => {
this._editor.changeViewZones((accessor) => {
this._disposeAllLenses(changeAccessor, accessor);
});
this._localToDispose.push(toDisposable(() => {
if (this._editor.getModel()) {
const scrollState = StableEditorScrollState.capture(this._editor);
this._editor.changeDecorations((changeAccessor) => {
this._editor.changeViewZones((accessor) => {
this._disposeAllLenses(changeAccessor, accessor);
});
scrollState.restore(this._editor);
} else {
// No accessors available
this._disposeAllLenses(null, null);
}
});
scrollState.restore(this._editor);
} else {
// No accessors available
this._disposeAllLenses(null, null);
}
});
}));
scheduler.schedule();
}
@@ -267,9 +264,9 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
}
private _onViewportChanged(): void {
if (this._currentFindOccPromise) {
this._currentFindOccPromise.cancel();
this._currentFindOccPromise = null;
if (this._currentResolveCodeLensSymbolsPromise) {
this._currentResolveCodeLensSymbolsPromise.cancel();
this._currentResolveCodeLensSymbolsPromise = null;
}
const model = this._editor.getModel();
@@ -291,24 +288,34 @@ export class CodeLensContribution implements editorCommon.IEditorContribution {
return;
}
const promises = toResolve.map((request, i) => {
this._currentResolveCodeLensSymbolsPromise = createCancelablePromise(token => {
const resolvedSymbols = new Array<ICodeLensSymbol>(request.length);
const promises = request.map((request, i) => {
return asWinJsPromise((token) => {
return request.provider.resolveCodeLens(model, request.symbol, token);
}).then(symbol => {
resolvedSymbols[i] = symbol;
const promises = toResolve.map((request, i) => {
const resolvedSymbols = new Array<ICodeLensSymbol>(request.length);
const promises = request.map((request, i) => {
if (typeof request.provider.resolveCodeLens === 'function') {
return Promise.resolve(request.provider.resolveCodeLens(model, request.symbol, token)).then(symbol => {
resolvedSymbols[i] = symbol;
});
}
resolvedSymbols[i] = request.symbol;
return Promise.resolve(void 0);
});
return Promise.all(promises).then(() => {
lenses[i].updateCommands(resolvedSymbols);
});
});
return TPromise.join(promises).then(() => {
lenses[i].updateCommands(resolvedSymbols);
});
return Promise.all(promises);
});
this._currentFindOccPromise = TPromise.join(promises).then(() => {
this._currentFindOccPromise = null;
this._currentResolveCodeLensSymbolsPromise.then(() => {
this._currentResolveCodeLensSymbolsPromise = null;
}).catch(err => {
this._currentResolveCodeLensSymbolsPromise = null;
onUnexpectedError(err);
});
}
}

View File

@@ -199,7 +199,7 @@ export class CodeLensHelper {
}
commit(changeAccessor: IModelDecorationsChangeAccessor): void {
var resultingDecorations = changeAccessor.deltaDecorations(this._removeDecorations, this._addDecorations);
let resultingDecorations = changeAccessor.deltaDecorations(this._removeDecorations, this._addDecorations);
for (let i = 0, len = resultingDecorations.length; i < len; i++) {
this._addDecorationsCallbacks[i](resultingDecorations[i]);
}

View File

@@ -13,6 +13,7 @@ import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { Range, IRange } from 'vs/editor/common/core/range';
import { illegalArgument } from 'vs/base/common/errors';
import { IModelService } from 'vs/editor/common/services/modelService';
import { CancellationToken } from 'vs/base/common/cancellation';
export interface IColorData {
@@ -20,10 +21,10 @@ export interface IColorData {
provider: DocumentColorProvider;
}
export function getColors(model: ITextModel): TPromise<IColorData[]> {
export function getColors(model: ITextModel, token: CancellationToken): Promise<IColorData[]> {
const colors: IColorData[] = [];
const providers = ColorProviderRegistry.ordered(model).reverse();
const promises = providers.map(provider => asWinJsPromise(token => provider.provideDocumentColors(model, token)).then(result => {
const promises = providers.map(provider => Promise.resolve(provider.provideDocumentColors(model, token)).then(result => {
if (Array.isArray(result)) {
for (let colorInfo of result) {
colors.push({ colorInfo, provider });
@@ -31,11 +32,11 @@ export function getColors(model: ITextModel): TPromise<IColorData[]> {
}
}));
return TPromise.join(promises).then(() => colors);
return Promise.all(promises).then(() => colors);
}
export function getColorPresentations(model: ITextModel, colorInfo: IColorInformation, provider: DocumentColorProvider): TPromise<IColorPresentation[]> {
return asWinJsPromise(token => provider.provideColorPresentations(model, colorInfo, token));
export function getColorPresentations(model: ITextModel, colorInfo: IColorInformation, provider: DocumentColorProvider, token: CancellationToken): Promise<IColorPresentation[]> {
return Promise.resolve(provider.provideColorPresentations(model, colorInfo, token));
}
registerLanguageCommand('_executeDocumentColorProvider', function (accessor, args) {

View File

@@ -6,7 +6,6 @@
import { RGBA } from 'vs/base/common/color';
import { hash } from 'vs/base/common/hash';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -17,6 +16,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService
import { getColors, IColorData } from 'vs/editor/contrib/colorPicker/color';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { TimeoutTimer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
const MAX_DECORATORS = 500;
@@ -28,8 +28,8 @@ export class ColorDetector implements IEditorContribution {
private _globalToDispose: IDisposable[] = [];
private _localToDispose: IDisposable[] = [];
private _computePromise: TPromise<void>;
private _timeoutPromise: TPromise<void>;
private _computePromise: CancelablePromise<void>;
private _timeoutTimer: TimeoutTimer;
private _decorationsIds: string[] = [];
private _colorDatas = new Map<string, IColorData>();
@@ -61,7 +61,7 @@ export class ColorDetector implements IEditorContribution {
}
}));
this._timeoutPromise = null;
this._timeoutTimer = null;
this._computePromise = null;
this._isEnabled = this.isEnabled();
this.onModelChanged();
@@ -115,29 +115,31 @@ export class ColorDetector implements IEditorContribution {
}
this._localToDispose.push(this._editor.onDidChangeModelContent((e) => {
if (!this._timeoutPromise) {
this._timeoutPromise = TPromise.timeout(ColorDetector.RECOMPUTE_TIME);
this._timeoutPromise.then(() => {
this._timeoutPromise = null;
if (!this._timeoutTimer) {
this._timeoutTimer = new TimeoutTimer();
this._timeoutTimer.cancelAndSet(() => {
this._timeoutTimer = null;
this.beginCompute();
});
}, ColorDetector.RECOMPUTE_TIME);
}
}));
this.beginCompute();
}
private beginCompute(): void {
this._computePromise = getColors(this._editor.getModel()).then(colorInfos => {
this.updateDecorations(colorInfos);
this.updateColorDecorators(colorInfos);
this._computePromise = null;
this._computePromise = createCancelablePromise(token => {
return getColors(this._editor.getModel(), token).then(colorInfos => {
this.updateDecorations(colorInfos);
this.updateColorDecorators(colorInfos);
this._computePromise = null;
});
});
}
private stop(): void {
if (this._timeoutPromise) {
this._timeoutPromise.cancel();
this._timeoutPromise = null;
if (this._timeoutTimer) {
this._timeoutTimer.cancel();
this._timeoutTimer = null;
}
if (this._computePromise) {
this._computePromise.cancel();

View File

@@ -57,7 +57,7 @@ export class ColorPickerHeader extends Disposable {
}
private onDidChangePresentation(): void {
this.pickedColorNode.textContent = this.model.presentation.label;
this.pickedColorNode.textContent = this.model.presentation ? this.model.presentation.label : '';
}
}

View File

@@ -12,6 +12,8 @@ import { registerEditorAction, IActionOptions, EditorAction, ServicesAccessor }
import { BlockCommentCommand } from './blockCommentCommand';
import { LineCommentCommand, Type } from './lineCommentCommand';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MenuId } from 'vs/platform/actions/common/actions';
abstract class CommentLineAction extends EditorAction {
@@ -28,11 +30,11 @@ abstract class CommentLineAction extends EditorAction {
return;
}
var commands: ICommand[] = [];
var selections = editor.getSelections();
var opts = model.getOptions();
let commands: ICommand[] = [];
let selections = editor.getSelections();
let opts = model.getOptions();
for (var i = 0; i < selections.length; i++) {
for (let i = 0; i < selections.length; i++) {
commands.push(new LineCommentCommand(selections[i], opts.tabSize, this._type));
}
@@ -52,7 +54,14 @@ class ToggleCommentLineAction extends CommentLineAction {
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.US_SLASH
primary: KeyMod.CtrlCmd | KeyCode.US_SLASH,
weight: KeybindingWeight.EditorContrib
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '5_insert',
title: nls.localize({ key: 'miToggleLineComment', comment: ['&& denotes a mnemonic'] }, "&&Toggle Line Comment"),
order: 1
}
});
}
@@ -67,7 +76,8 @@ class AddLineCommentAction extends CommentLineAction {
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_C),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -82,7 +92,8 @@ class RemoveLineCommentAction extends CommentLineAction {
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_U),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -99,16 +110,23 @@ class BlockCommentAction extends EditorAction {
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_A,
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A }
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_A },
weight: KeybindingWeight.EditorContrib
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '5_insert',
title: nls.localize({ key: 'miToggleBlockComment', comment: ['&& denotes a mnemonic'] }, "Toggle &&Block Comment"),
order: 2
}
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
var commands: ICommand[] = [];
var selections = editor.getSelections();
let commands: ICommand[] = [];
let selections = editor.getSelections();
for (var i = 0; i < selections.length; i++) {
for (let i = 0; i < selections.length; i++) {
commands.push(new BlockCommentCommand(selections[i]));
}

View File

@@ -93,16 +93,9 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Also, build up several offsets and lengths useful in the generation of editor operations.
*/
public static _analyzeLines(type: Type, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData {
var lineData: ILinePreflightData,
lineContentStartOffset: number,
commentStrEndOffset: number,
i: number,
lineCount: number,
lineNumber: number,
shouldRemoveComments: boolean,
lineContent: string,
onlyWhitespaceLines = true;
let onlyWhitespaceLines = true;
let shouldRemoveComments: boolean;
if (type === Type.Toggle) {
shouldRemoveComments = true;
} else if (type === Type.ForceAdd) {
@@ -111,12 +104,12 @@ export class LineCommentCommand implements editorCommon.ICommand {
shouldRemoveComments = true;
}
for (i = 0, lineCount = lines.length; i < lineCount; i++) {
lineData = lines[i];
lineNumber = startLineNumber + i;
for (let i = 0, lineCount = lines.length; i < lineCount; i++) {
const lineData = lines[i];
const lineNumber = startLineNumber + i;
lineContent = model.getLineContent(lineNumber);
lineContentStartOffset = strings.firstNonWhitespaceIndex(lineContent);
const lineContent = model.getLineContent(lineNumber);
const lineContentStartOffset = strings.firstNonWhitespaceIndex(lineContent);
if (lineContentStartOffset === -1) {
// Empty or whitespace only line
@@ -147,7 +140,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
}
if (shouldRemoveComments) {
commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength;
const commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength;
if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) {
lineData.commentStrLength += 1;
}
@@ -159,7 +152,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
shouldRemoveComments = false;
// Also, no longer ignore them
for (i = 0, lineCount = lines.length; i < lineCount; i++) {
for (let i = 0, lineCount = lines.length; i < lineCount; i++) {
lines[i].ignore = false;
}
}
@@ -175,7 +168,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments
*/
public static _gatherPreflightData(type: Type, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData {
var lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber);
const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber);
if (lines === null) {
return {
supported: false,
@@ -192,7 +185,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
*/
private _executeLineComments(model: ISimpleModel, builder: editorCommon.IEditOperationBuilder, data: IPreflightData, s: Selection): void {
var ops: IIdentifiedSingleEditOperation[];
let ops: IIdentifiedSingleEditOperation[];
if (data.shouldRemoveComments) {
ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber);
@@ -203,7 +196,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
const cursorPosition = new Position(s.positionLineNumber, s.positionColumn);
for (var i = 0, len = ops.length; i < len; i++) {
for (let i = 0, len = ops.length; i < len; i++) {
builder.addEditOperation(ops[i].range, ops[i].text);
if (ops[i].range.isEmpty() && ops[i].range.getStartPosition().equals(cursorPosition)) {
const lineContent = model.getLineContent(cursorPosition.lineNumber);
@@ -278,14 +271,14 @@ export class LineCommentCommand implements editorCommon.ICommand {
return;
}
var startToken = config.blockCommentStartToken;
var endToken = config.blockCommentEndToken;
const startToken = config.blockCommentStartToken;
const endToken = config.blockCommentEndToken;
var ops = this._attemptRemoveBlockComment(model, s, startToken, endToken);
let ops = this._attemptRemoveBlockComment(model, s, startToken, endToken);
if (!ops) {
if (s.isEmpty()) {
var lineContent = model.getLineContent(s.startLineNumber);
var firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
const lineContent = model.getLineContent(s.startLineNumber);
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
if (firstNonWhitespaceIndex === -1) {
// Line is empty or contains only whitespace
firstNonWhitespaceIndex = lineContent.length;
@@ -305,14 +298,14 @@ export class LineCommentCommand implements editorCommon.ICommand {
}
}
this._selectionId = builder.trackSelection(s);
for (var i = 0; i < ops.length; i++) {
for (let i = 0; i < ops.length; i++) {
builder.addEditOperation(ops[i].range, ops[i].text);
}
}
public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void {
var s = this._selection;
let s = this._selection;
this._moveEndPositionDown = false;
if (s.startLineNumber < s.endLineNumber && s.endColumn === 1) {
@@ -320,7 +313,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1));
}
var data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber);
const data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber);
if (data.supported) {
return this._executeLineComments(model, builder, data, s);
}
@@ -329,7 +322,7 @@ export class LineCommentCommand implements editorCommon.ICommand {
}
public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection {
var result = helper.getTrackedSelection(this._selectionId);
let result = helper.getTrackedSelection(this._selectionId);
if (this._moveEndPositionDown) {
result = result.setEndPosition(result.endLineNumber + 1, 1);
@@ -347,13 +340,10 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Generate edit operations in the remove line comment case
*/
public static _createRemoveLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
var i: number,
len: number,
lineData: ILinePreflightData,
res: IIdentifiedSingleEditOperation[] = [];
let res: IIdentifiedSingleEditOperation[] = [];
for (i = 0, len = lines.length; i < len; i++) {
lineData = lines[i];
for (let i = 0, len = lines.length; i < len; i++) {
const lineData = lines[i];
if (lineData.ignore) {
continue;
@@ -372,13 +362,10 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Generate edit operations in the add line comment case
*/
public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] {
var i: number,
len: number,
lineData: ILinePreflightData,
res: IIdentifiedSingleEditOperation[] = [];
let res: IIdentifiedSingleEditOperation[] = [];
for (i = 0, len = lines.length; i < len; i++) {
lineData = lines[i];
for (let i = 0, len = lines.length; i < len; i++) {
const lineData = lines[i];
if (lineData.ignore) {
continue;
@@ -402,23 +389,19 @@ export class LineCommentCommand implements editorCommon.ICommand {
* Adjust insertion points to have them vertically aligned in the add line comment case
*/
public static _normalizeInsertionPoint(model: ISimpleModel, lines: IInsertionPoint[], startLineNumber: number, tabSize: number): void {
var minVisibleColumn = Number.MAX_VALUE,
i: number,
len: number,
lineContent: string,
j: number,
lenJ: number,
currentVisibleColumn: number;
let minVisibleColumn = Number.MAX_VALUE;
let j: number;
let lenJ: number;
for (i = 0, len = lines.length; i < len; i++) {
for (let i = 0, len = lines.length; i < len; i++) {
if (lines[i].ignore) {
continue;
}
lineContent = model.getLineContent(startLineNumber + i);
const lineContent = model.getLineContent(startLineNumber + i);
currentVisibleColumn = 0;
for (j = 0, lenJ = lines[i].commentStrOffset; currentVisibleColumn < minVisibleColumn && j < lenJ; j++) {
let currentVisibleColumn = 0;
for (let j = 0, lenJ = lines[i].commentStrOffset; currentVisibleColumn < minVisibleColumn && j < lenJ; j++) {
currentVisibleColumn = LineCommentCommand.nextVisibleColumn(currentVisibleColumn, tabSize, lineContent.charCodeAt(j) === CharCode.Tab, 1);
}
@@ -429,14 +412,14 @@ export class LineCommentCommand implements editorCommon.ICommand {
minVisibleColumn = Math.floor(minVisibleColumn / tabSize) * tabSize;
for (i = 0, len = lines.length; i < len; i++) {
for (let i = 0, len = lines.length; i < len; i++) {
if (lines[i].ignore) {
continue;
}
lineContent = model.getLineContent(startLineNumber + i);
const lineContent = model.getLineContent(startLineNumber + i);
currentVisibleColumn = 0;
let currentVisibleColumn = 0;
for (j = 0, lenJ = lines[i].commentStrOffset; currentVisibleColumn < minVisibleColumn && j < lenJ; j++) {
currentVisibleColumn = LineCommentCommand.nextVisibleColumn(currentVisibleColumn, tabSize, lineContent.charCodeAt(j) === CharCode.Tab, 1);
}

View File

@@ -74,7 +74,7 @@ suite('Editor Contrib - Line Comment Command', () => {
function createBasicLinePreflightData(commentTokens: string[]): ILinePreflightData[] {
return commentTokens.map((commentString) => {
var r: ILinePreflightData = {
const r: ILinePreflightData = {
ignore: false,
commentStr: commentString,
commentStrOffset: 0,
@@ -85,7 +85,7 @@ suite('Editor Contrib - Line Comment Command', () => {
}
test('_analyzeLines', function () {
var r: IPreflightData;
let r: IPreflightData;
r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([
'\t\t',
@@ -151,16 +151,16 @@ suite('Editor Contrib - Line Comment Command', () => {
test('_normalizeInsertionPoint', function () {
var runTest = (mixedArr: any[], tabSize: number, expected: number[], testName: string) => {
var model = createSimpleModel(mixedArr.filter((item, idx) => idx % 2 === 0));
var offsets = mixedArr.filter((item, idx) => idx % 2 === 1).map(offset => {
const runTest = (mixedArr: any[], tabSize: number, expected: number[], testName: string) => {
const model = createSimpleModel(mixedArr.filter((item, idx) => idx % 2 === 0));
const offsets = mixedArr.filter((item, idx) => idx % 2 === 1).map(offset => {
return {
commentStrOffset: offset,
ignore: false
};
});
LineCommentCommand._normalizeInsertionPoint(model, offsets, 1, tabSize);
var actual = offsets.map(item => item.commentStrOffset);
const actual = offsets.map(item => item.commentStrOffset);
assert.deepEqual(actual, expected, testName);
};

View File

@@ -20,6 +20,7 @@ import { IEditorContribution, IScrollEvent, ScrollType } from 'vs/editor/common/
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export interface IPosition {
x: number;
@@ -93,7 +94,7 @@ export class ContextMenuController implements IEditorContribution {
}
// Unless the user triggerd the context menu through Shift+F10, use the mouse position as menu position
var forcedPosition: IPosition;
let forcedPosition: IPosition;
if (e.target.type !== MouseTargetType.TEXTAREA) {
forcedPosition = { x: e.event.posx, y: e.event.posy + 1 };
}
@@ -113,7 +114,7 @@ export class ContextMenuController implements IEditorContribution {
}
// Find actions available for menu
var menuActions = this._getMenuActions();
const menuActions = this._getMenuActions();
// Show menu if we have actions to show
if (menuActions.length > 0) {
@@ -140,23 +141,25 @@ export class ContextMenuController implements IEditorContribution {
private _doShowContextMenu(actions: IAction[], forcedPosition: IPosition = null): void {
// Disable hover
var oldHoverSetting = this._editor.getConfiguration().contribInfo.hover;
const oldHoverSetting = this._editor.getConfiguration().contribInfo.hover;
this._editor.updateOptions({
hover: false
hover: {
enabled: false
}
});
var menuPosition = forcedPosition;
let menuPosition = forcedPosition;
if (!menuPosition) {
// Ensure selection is visible
this._editor.revealPosition(this._editor.getPosition(), ScrollType.Immediate);
this._editor.render();
var cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition());
const cursorCoords = this._editor.getScrolledVisiblePosition(this._editor.getPosition());
// Translate to absolute editor position
var editorCoords = dom.getDomNodePagePosition(this._editor.getDomNode());
var posx = editorCoords.left + cursorCoords.left;
var posy = editorCoords.top + cursorCoords.top + cursorCoords.height;
const editorCoords = dom.getDomNodePagePosition(this._editor.getDomNode());
const posx = editorCoords.left + cursorCoords.left;
const posy = editorCoords.top + cursorCoords.top + cursorCoords.height;
menuPosition = { x: posx, y: posy };
}
@@ -171,12 +174,12 @@ export class ContextMenuController implements IEditorContribution {
},
getActionItem: (action) => {
var keybinding = this._keybindingFor(action);
const keybinding = this._keybindingFor(action);
if (keybinding) {
return new ActionItem(action, action, { label: true, keybinding: keybinding.getLabel(), isMenu: true });
}
var customActionItem = <any>action;
const customActionItem = <any>action;
if (typeof customActionItem.getActionItem === 'function') {
return customActionItem.getActionItem();
}
@@ -225,7 +228,8 @@ class ShowContextMenu extends EditorAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.Shift | KeyCode.F10
primary: KeyMod.Shift | KeyCode.F10,
weight: KeybindingWeight.EditorContrib
}
});
}

View File

@@ -4,13 +4,15 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { Selection } from 'vs/editor/common/core/selection';
import { registerEditorCommand, ServicesAccessor, EditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { ServicesAccessor, registerEditorContribution, EditorAction, registerEditorAction } from 'vs/editor/browser/editorExtensions';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
class CursorState {
readonly selections: Selection[];
@@ -108,22 +110,25 @@ export class CursorUndoController extends Disposable implements IEditorContribut
}
}
export class CursorUndo extends EditorCommand {
export class CursorUndo extends EditorAction {
constructor() {
super({
id: 'cursorUndo',
label: nls.localize('cursor.undo', "Soft Undo"),
alias: 'Soft Undo',
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.textInputFocus,
primary: KeyMod.CtrlCmd | KeyCode.KEY_U
primary: KeyMod.CtrlCmd | KeyCode.KEY_U,
weight: KeybindingWeight.EditorContrib
}
});
}
public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
public run(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void {
CursorUndoController.get(editor).cursorUndo();
}
}
registerEditorContribution(CursorUndoController);
registerEditorCommand(new CursorUndo());
registerEditorAction(CursorUndo);

View File

@@ -19,6 +19,15 @@ import { Selection } from 'vs/editor/common/core/selection';
import { DragAndDropCommand } from 'vs/editor/contrib/dnd/dragAndDropCommand';
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { IModelDeltaDecoration } from 'vs/editor/common/model';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean {
if (isMacintosh) {
return e.altKey;
} else {
return e.ctrlKey;
}
}
export class DragAndDropController implements editorCommon.IEditorContribution {
@@ -30,7 +39,6 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
private _dndDecorationIds: string[];
private _mouseDown: boolean;
private _modiferPressed: boolean;
static TRIGGER_MODIFIER = isMacintosh ? 'altKey' : 'ctrlKey';
static TRIGGER_KEY_VALUE = isMacintosh ? KeyCode.Alt : KeyCode.Ctrl;
static get(editor: ICodeEditor): DragAndDropController {
@@ -57,11 +65,11 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
return;
}
if (e[DragAndDropController.TRIGGER_MODIFIER]) {
if (hasTriggerModifier(e)) {
this._modiferPressed = true;
}
if (this._mouseDown && e[DragAndDropController.TRIGGER_MODIFIER]) {
if (this._mouseDown && hasTriggerModifier(e)) {
this._editor.updateOptions({
mouseStyle: 'copy'
});
@@ -73,7 +81,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
return;
}
if (e[DragAndDropController.TRIGGER_MODIFIER]) {
if (hasTriggerModifier(e)) {
this._modiferPressed = false;
}
@@ -108,7 +116,7 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
}
}
if (mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER]) {
if (hasTriggerModifier(mouseEvent.event)) {
this._editor.updateOptions({
mouseStyle: 'copy'
});
@@ -147,14 +155,14 @@ export class DragAndDropController implements editorCommon.IEditorContribution {
} else if (!this._dragSelection.containsPosition(newCursorPosition) ||
(
(
mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER] ||
hasTriggerModifier(mouseEvent.event) ||
this._modiferPressed
) && (
this._dragSelection.getEndPosition().equals(newCursorPosition) || this._dragSelection.getStartPosition().equals(newCursorPosition)
) // we allow users to paste content beside the selection
)) {
this._editor.pushUndoStop();
this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, mouseEvent.event[DragAndDropController.TRIGGER_MODIFIER] || this._modiferPressed));
this._editor.executeCommand(DragAndDropController.ID, new DragAndDropCommand(this._dragSelection, newCursorPosition, hasTriggerModifier(mouseEvent.event) || this._modiferPressed));
this._editor.pushUndoStop();
}
}

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 14H0V2h16v12z" id="outline"/><path class="icon-vs-bg" d="M1 3v10h14V3H1zm13 9H8V8.507l3.998.006-2.121 2.129.707.707 3.35-3.35-3.35-3.338-.707.706 2.138 2.146L8 7.507V4h6v8z" id="iconBg"/><path class="icon-vs-fg" d="M14 4v8H8V8.507l3.998.006-2.121 2.129.707.707 3.35-3.35-3.35-3.338-.707.706 2.138 2.146L8 7.507V4h6zM6.057 5.367L5.35 4.66 2 8.01l3.35 3.338.707-.707-2.139-2.146L8 8.501v-1l-4.064-.005 2.121-2.129z" id="iconFg"/></svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2a292c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 14H0V2h16v12z" id="outline"/><path class="icon-vs-bg" d="M1 3v10h14V3H1zm13 9H8V8.507l3.998.006-2.121 2.129.707.707 3.35-3.35-3.35-3.338-.707.706 2.138 2.146L8 7.507V4h6v8z" id="iconBg"/><path class="icon-vs-fg" d="M14 4v8H8V8.507l3.998.006-2.121 2.129.707.707 3.35-3.35-3.35-3.338-.707.706 2.138 2.146L8 7.507V4h6zM6.057 5.367L5.35 4.66 2 8.01l3.35 3.338.707-.707-2.139-2.146L8 8.501v-1l-4.064-.005 2.121-2.129z" id="iconFg"/></svg>

After

Width:  |  Height:  |  Size: 738 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-action-orange{fill:#c27d1a}</style><g id="canvas"><path id="XMLID_1_" class="icon-canvas-transparent" d="M16 16H0V0h16v16z"/></g><path class="icon-vs-out" d="M16 6.586l-3-3L11.586 5H9.414l1-1-4-4h-.828L0 5.586v.828l4 4L6.414 8H7v5h1.586l3 3h.828L16 12.414v-.828L13.914 9.5 16 7.414v-.828z" id="outline"/><g id="iconBg"><path class="icon-vs-action-orange" d="M13 10l2 2-3 3-2-2 1-1H8V7H6L4 9 1 6l5-5 3 3-2 2h5l1-1 2 2-3 3-2-2 1-1H9v4l2.999.002L13 10z"/></g></svg>

After

Width:  |  Height:  |  Size: 612 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-action-orange{fill:#e8ab53}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 6.586l-3-3L11.586 5H9.414l1-1-4-4h-.828L0 5.586v.828l4 4L6.414 8H7v5h1.586l3 3h.828L16 12.414v-.828L13.914 9.5 16 7.414v-.828z" id="outline"/><path class="icon-vs-action-orange" d="M13 10l2 2-3 3-2-2 1-1H8V7H6L4 9 1 6l5-5 3 3-2 2h5l1-1 2 2-3 3-2-2 1-1H9v4l2.999.002L13 10z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 584 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-red{fill:#e51400}.icon-vs-yellow{fill:#ffcc00}.icon-vs-green{fill:#339933}.icon-vs-blue{fill:#1ba1e2}.icon-vs-action-purple{fill:#652d90}.icon-white{fill:#ffffff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 8c0 4.411-3.589 8-8 8a2.803 2.803 0 0 1-2.8-2.8c0-.833.272-1.629.766-2.241a.596.596 0 0 0 .101-.359.667.667 0 0 0-.667-.666.58.58 0 0 0-.358.102A3.584 3.584 0 0 1 2.8 10.8 2.803 2.803 0 0 1 0 8c0-4.411 3.589-8 8-8s8 3.589 8 8z" id="outline"/><path class="icon-white" d="M5.4 7.933a2.67 2.67 0 0 1 2.667 2.666c0 .606-.193 1.179-.544 1.614a1.599 1.599 0 0 0-.323.987.8.8 0 0 0 .8.8c3.309 0 6-2.691 6-6s-2.691-6-6-6-6 2.691-6 6c0 .441.359.8.8.8.378 0 .729-.114.986-.322A2.568 2.568 0 0 1 5.4 7.933z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M8 15c-.992 0-1.8-.808-1.8-1.8 0-.606.193-1.179.544-1.613.208-.259.323-.609.323-.987 0-.919-.748-1.666-1.667-1.666-.377 0-.728.115-.986.323A2.58 2.58 0 0 1 2.8 9.8C1.808 9.8 1 8.992 1 8c0-3.86 3.14-7 7-7 3.859 0 7 3.14 7 7 0 3.859-3.141 7-7 7zM5.4 7.933a2.67 2.67 0 0 1 2.667 2.666c0 .606-.193 1.179-.544 1.614a1.599 1.599 0 0 0-.323.987.8.8 0 0 0 .8.8c3.309 0 6-2.691 6-6s-2.691-6-6-6-6 2.691-6 6c0 .441.359.8.8.8.378 0 .729-.114.986-.322A2.568 2.568 0 0 1 5.4 7.933z"/><path class="icon-vs-action-purple" d="M4.5 5.375a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-blue" d="M7.125 3.625a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-green" d="M10.625 4.5a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-yellow" d="M11.5 8a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-red" d="M9.75 10.625a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}.icon-vs-red{fill:#ff4635}.icon-vs-yellow{fill:#ffda48}.icon-vs-green{fill:#78d278}.icon-vs-blue{fill:#37aee7}.icon-vs-action-purple{fill:#b180d7}.icon-white{fill:#000000}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 8c0 4.411-3.589 8-8 8a2.803 2.803 0 0 1-2.8-2.8c0-.833.272-1.629.766-2.241a.596.596 0 0 0 .101-.359.667.667 0 0 0-.667-.666.58.58 0 0 0-.358.102A3.584 3.584 0 0 1 2.8 10.8 2.803 2.803 0 0 1 0 8c0-4.411 3.589-8 8-8s8 3.589 8 8z" id="outline"/><path class="icon-white" d="M5.4 7.933a2.67 2.67 0 0 1 2.667 2.666c0 .606-.193 1.179-.544 1.614a1.599 1.599 0 0 0-.323.987.8.8 0 0 0 .8.8c3.309 0 6-2.691 6-6s-2.691-6-6-6-6 2.691-6 6c0 .441.359.8.8.8.378 0 .729-.114.986-.322A2.568 2.568 0 0 1 5.4 7.933z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M8 15c-.992 0-1.8-.808-1.8-1.8 0-.606.193-1.179.544-1.613.208-.259.323-.609.323-.987 0-.919-.748-1.666-1.667-1.666-.377 0-.728.115-.986.323A2.58 2.58 0 0 1 2.8 9.8C1.808 9.8 1 8.992 1 8c0-3.86 3.14-7 7-7 3.859 0 7 3.14 7 7 0 3.859-3.141 7-7 7zM5.4 7.933a2.67 2.67 0 0 1 2.667 2.666c0 .606-.193 1.179-.544 1.614a1.599 1.599 0 0 0-.323.987.8.8 0 0 0 .8.8c3.309 0 6-2.691 6-6s-2.691-6-6-6-6 2.691-6 6c0 .441.359.8.8.8.378 0 .729-.114.986-.322A2.568 2.568 0 0 1 5.4 7.933z"/><path class="icon-vs-action-purple" d="M4.5 5.375a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-blue" d="M7.125 3.625a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-green" d="M10.625 4.5a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-yellow" d="M11.5 8a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/><path class="icon-vs-red" d="M9.75 10.625a.875.875 0 1 0 0 1.75.875.875 0 0 0 0-1.75z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M2.879 14L1 12.121V3.879L2.879 2h10.242L15 3.879v8.242L13.121 14H2.879z" id="outline"/><path class="icon-vs-fg" d="M12.293 4H3.707L3 4.707v6.586l.707.707h8.586l.707-.707V4.707L12.293 4zM11 10H5V9h6v1zm0-3H5V6h6v1z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M12.707 13H3.293L2 11.707V4.293L3.293 3h9.414L14 4.293v7.414L12.707 13zm-9-1h8.586l.707-.707V4.707L12.293 4H3.707L3 4.707v6.586l.707.707z"/><path class="icon-vs-action-blue" d="M11 7H5V6h6v1zm0 2H5v1h6V9z"/></g></svg>

After

Width:  |  Height:  |  Size: 823 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M2.879 14L1 12.121V3.879L2.879 2h10.242L15 3.879v8.242L13.121 14H2.879z" id="outline"/><path class="icon-vs-fg" d="M12.293 4H3.707L3 4.707v6.586l.707.707h8.586l.707-.707V4.707L12.293 4zM11 10H5V9h6v1zm0-3H5V6h6v1z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M12.707 13H3.293L2 11.707V4.293L3.293 3h9.414L14 4.293v7.414L12.707 13zm-9-1h8.586l.707-.707V4.707L12.293 4H3.707L3 4.707v6.586l.707.707z"/><path class="icon-vs-action-blue" d="M11 7H5V6h6v1zm0 2H5v1h6V9z"/></g></svg>

After

Width:  |  Height:  |  Size: 823 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#424242;} .icon-vs-fg_x0020_2{fill:#F0EFF1;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 16h-13v-16h8.621l4.379 4.379v11.621z" id="outline"/><path class="icon-vs-fg_x0020_2" d="M13 14h-9v-12h5v4h4v8zm-3-9v-2.793l2.793 2.793h-2.793z" id="iconFg"/><path class="icon-vs-bg" d="M3 1v14h11v-10.207l-3.793-3.793h-7.207zm10 13h-9v-12h5v4h4v8zm-3-9v-2.793l2.793 2.793h-2.793z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-bg{fill:#c5c5c5;} .icon-vs-fg_x0020_2{fill:#F0EFF1;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M15 16h-13v-16h8.621l4.379 4.379v11.621z" id="outline"/><path class="icon-vs-fg_x0020_2" d="M13 14h-9v-12h5v4h4v8zm-3-9v-2.793l2.793 2.793h-2.793z" id="iconFg"/><path class="icon-vs-bg" d="M3 1v14h11v-10.207l-3.793-3.793h-7.207zm10 13h-9v-12h5v4h4v8zm-3-9v-2.793l2.793 2.793h-2.793z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 673 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 15V6h6V2.586L7.585 1h6.829L16 2.586v5.829L14.414 10H10v5H0zm3-6z" id="outline"/><path class="icon-vs-fg" d="M8 3v3h5v1h-3v1h4V3H8zm5 2H9V4h4v1zM2 8v5h6V8H2zm5 3H3v-1h4v1z" id="iconFg"/><path class="icon-vs-action-blue" d="M10 6h3v1h-3V6zM9 4v1h4V4H9zm5-2H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1zm-7 8H3v1h4v-1zm2-3v7H1V7h8zM8 8H2v5h6V8z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 664 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 15V6h6V2.586L7.585 1h6.829L16 2.586v5.829L14.414 10H10v5H0zm3-6z" id="outline"/><path class="icon-vs-fg" d="M8 3v3h5v1h-3v1h4V3H8zm5 2H9V4h4v1zM2 8v5h6V8H2zm5 3H3v-1h4v1z" id="iconFg"/><path class="icon-vs-action-blue" d="M10 6h3v1h-3V6zM9 4v1h4V4H9zm5-2H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1zm-7 8H3v1h4v-1zm2-3v7H1V7h8zM8 8H2v5h6V8z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 664 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-orange{fill:#c27d1a}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14.414 1L16 2.586v5.828L14.414 10H10v3.416L8.414 15H1.586L0 13.416v-5.83L1.586 6H6V2.586L7.586 1h6.828z" id="outline"/><path class="icon-vs-fg" d="M2 13h6V8H2v5zm1-4h4v1H3V9zm0 2h4v1H3v-1zm11-5V3H8v3h.414L9 6.586V6h4v1H9.414l.586.586V8h4V6zm-1-1H9V4h4v1z" id="iconFg"/><path class="icon-vs-action-orange" d="M3 11h4.001v1H3v-1zm0-1h4.001V9H3v1zm6-2v5l-1 1H2l-1-1V8l1-1h6l1 1zM8 8H2v5h6V8zm1-2l1 1h3V6H9zm0-1h4V4H9v1zm5-3H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-orange{fill:#e8ab53}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14.414 1L16 2.586v5.828L14.414 10H10v3.416L8.414 15H1.586L0 13.416v-5.83L1.586 6H6V2.586L7.586 1h6.828z" id="outline"/><path class="icon-vs-fg" d="M2 13h6V8H2v5zm1-4h4v1H3V9zm0 2h4v1H3v-1zm11-5V3H8v3h.414L9 6.586V6h4v1H9.414l.586.586V8h4V6zm-1-1H9V4h4v1z" id="iconFg"/><path class="icon-vs-action-orange" d="M3 11h4.001v1H3v-1zm0-1h4.001V9H3v1zm6-2v5l-1 1H2l-1-1V8l1-1h6l1 1zM8 8H2v5h6V8zm1-2l1 1h3V6H9zm0-1h4V4H9v1zm5-3H8L7 3v3h1V3h6v5h-4v1h4l1-1V3l-1-1z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-action-orange{fill:#c27d1a}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14 1.414L9.414 6H14v1.414L5.414 16H3v-1.234L5.371 10H2V8.764L6.382 0H14v1.414z" id="outline" style="display: none;"/><path class="icon-vs-action-orange" d="M7 7h6l-8 8H4l2.985-6H3l4-8h6L7 7z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 499 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-action-orange{fill:#e8ab53}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M14 1.414L9.414 6H14v1.414L5.414 16H3v-1.234L5.371 10H2V8.764L6.382 0H14v1.414z" id="outline" style="display: none;"/><path class="icon-vs-action-orange" d="M7 7h6l-8 8H4l2.985-6H3l4-8h6L7 7z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 499 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 10.736v-6.236l9-4.5 7 3.5v6.236l-9 4.5-7-3.5z" id="outline"/><path class="icon-vs-action-blue" d="M9 1l-8 4v5l6 3 8-4v-5l-6-3zm-2 5.882l-3.764-1.882 5.764-2.882 3.764 1.882-5.764 2.882z" id="iconBg"/><path class="icon-vs-fg" d="M9 2.118l3.764 1.882-5.764 2.882-3.764-1.882 5.764-2.882z" id="iconFg"/></svg>

After

Width:  |  Height:  |  Size: 680 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M0 10.736V4.5L9 0l7 3.5v6.236l-9 4.5-7-3.5z" id="outline"/><path class="icon-vs-action-blue" d="M9 1L1 5v5l6 3 8-4V4L9 1zM7 6.882L3.236 5 9 2.118 12.764 4 7 6.882z" id="iconBg"/><path class="icon-vs-fg" d="M9 2.118L12.764 4 7 6.882 3.236 5 9 2.118z" id="iconFg"/></svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11 15v-3h2V4h-2V1h5v14h-5zM0 15V1h5v3H3v8h2v3H0z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M4 14H1V2h3v1H2v10h2v1zM15 2h-3v1h2v10h-2v1h3V2z"/></g></svg>

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11 15v-3h2V4h-2V1h5v14h-5zM0 15V1h5v3H3v8h2v3H0z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M4 14H1V2h3v1H2v10h2v1zM15 2h-3v1h2v10h-2v1h3V2z"/></g></svg>

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 5V2H9V1H0v14h13v-3h3V9h-1V6H9V5h7zm-8 7V9h1v3H8z" id="outline"/><path class="icon-vs-fg" d="M2 3h5v1H2V3z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M15 4h-5V3h5v1zm-1 3h-2v1h2V7zm-4 0H1v1h9V7zm2 6H1v1h11v-1zm-5-3H1v1h6v-1zm8 0h-5v1h5v-1zM8 2v3H1V2h7zM7 3H2v1h5V3z"/></g></svg>

After

Width:  |  Height:  |  Size: 596 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2a292c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 5V2H9V1H0v14h13v-3h3V9h-1V6H9V5h7zm-8 7V9h1v3H8z" id="outline"/><path class="icon-vs-fg" d="M2 3h5v1H2V3z" id="iconFg"/><g id="iconBg"><path class="icon-vs-bg" d="M15 4h-5V3h5v1zm-1 3h-2v1h2V7zm-4 0H1v1h9V7zm2 6H1v1h11v-1zm-5-3H1v1h6v-1zm8 0h-5v1h5v-1zM8 2v3H1V2h7zM7 3H2v1h5V3z"/></g></svg>

After

Width:  |  Height:  |  Size: 596 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" enable-background="new 0 0 16 16"><style type="text/css">.icon-canvas-transparent{opacity:0;fill:#F6F6F6;} .icon-vs-out{fill:#F6F6F6;} .icon-vs-fg{fill:#F0EFF1;} .icon-vs-action-blue{fill:#00539C;}</style><path class="icon-canvas-transparent" d="M16 16h-16v-16h16v16z" id="canvas"/><path class="icon-vs-out" d="M11.5 12c-1.915 0-3.602-1.241-4.228-3h-1.41c-.536.985-1.572 1.625-2.737 1.625-1.723 0-3.125-1.402-3.125-3.125s1.402-3.125 3.125-3.125c1.165 0 2.201.639 2.737 1.625h1.41c.626-1.759 2.313-3 4.228-3 2.481 0 4.5 2.019 4.5 4.5s-2.019 4.5-4.5 4.5z" id="outline"/><path class="icon-vs-fg" d="M11.5 9c-.827 0-1.5-.674-1.5-1.5 0-.828.673-1.5 1.5-1.5s1.5.672 1.5 1.5c0 .826-.673 1.5-1.5 1.5z" id="iconFg"/><path class="icon-vs-action-blue" d="M11.5 4c-1.762 0-3.205 1.306-3.45 3h-2.865c-.226-.931-1.059-1.625-2.06-1.625-1.174 0-2.125.951-2.125 2.125s.951 2.125 2.125 2.125c1 0 1.834-.694 2.06-1.625h2.865c.245 1.694 1.688 3 3.45 3 1.933 0 3.5-1.567 3.5-3.5s-1.567-3.5-3.5-3.5zm0 5c-.827 0-1.5-.673-1.5-1.5s.673-1.5 1.5-1.5 1.5.673 1.5 1.5-.673 1.5-1.5 1.5z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11.5 12c-1.915 0-3.602-1.241-4.228-3h-1.41a3.11 3.11 0 0 1-2.737 1.625C1.402 10.625 0 9.223 0 7.5s1.402-3.125 3.125-3.125c1.165 0 2.201.639 2.737 1.625h1.41c.626-1.759 2.313-3 4.228-3C13.981 3 16 5.019 16 7.5S13.981 12 11.5 12z" id="outline"/><path class="icon-vs-fg" d="M11.5 9A1.501 1.501 0 1 1 13 7.5c0 .826-.673 1.5-1.5 1.5z" id="iconFg"/><path class="icon-vs-action-blue" d="M11.5 4a3.49 3.49 0 0 0-3.45 3H5.185A2.122 2.122 0 0 0 1 7.5a2.123 2.123 0 1 0 4.185.5H8.05a3.49 3.49 0 0 0 3.45 3 3.5 3.5 0 1 0 0-7zm0 5c-.827 0-1.5-.673-1.5-1.5S10.673 6 11.5 6s1.5.673 1.5 1.5S12.327 9 11.5 9z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 923 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11 3v1.015L8.733 2.882 5 4.749V3H0v10h5v-1.859l2.156 1.077L11 10.295V13h5V3h-5z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M2 5v6h2v1H1V4h3v1H2zm10 6v1h3V4h-3v1h2v6h-2z" id="iconBg"/><path class="icon-vs-fg" d="M7.156 7.156l-1.578-.789 3.156-1.578 1.578.789-3.156 1.578z" id="iconFg" style="display: none;"/><path class="icon-vs-action-blue" d="M8.733 4L4 6.367v3.156L7.156 11.1l4.733-2.367V5.578L8.733 4zM7.156 7.156l-1.578-.789 3.156-1.578 1.578.789-3.156 1.578z" id="colorImportance"/></svg>

After

Width:  |  Height:  |  Size: 853 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M11 3v1.015L8.733 2.882 5 4.749V3H0v10h5v-1.859l2.156 1.077L11 10.295V13h5V3h-5z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M2 5v6h2v1H1V4h3v1H2zm10 6v1h3V4h-3v1h2v6h-2z" id="iconBg"/><path class="icon-vs-fg" d="M7.156 7.156l-1.578-.789 3.156-1.578 1.578.789-3.156 1.578z" id="iconFg" style="display: none;"/><path class="icon-vs-action-blue" d="M8.733 4L4 6.367v3.156L7.156 11.1l4.733-2.367V5.578L8.733 4zM7.156 7.156l-1.578-.789 3.156-1.578 1.578.789-3.156 1.578z" id="colorImportance"/></svg>

After

Width:  |  Height:  |  Size: 853 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-fg{fill:#f0eff1;}.icon-vs-action-purple{fill:#652d90;}</style></defs><title>Method_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline"><path class="icon-vs-out" d="M15,3.349v8.4L8.975,16h-.9L1,11.582V3.327L7.6,0H8.713Z"/></g><g id="iconFg"><path class="icon-vs-fg" d="M12.715,4.4,8.487,7.02,3.565,4.272,8.144,1.963ZM3,5.1,8,7.894v5.7L3,10.473Zm6,8.434V7.878L13,5.4v5.318Z"/></g><g id="iconBg"><path class="icon-vs-action-purple" d="M8.156.837,2,3.942v7.085L8.517,15.1,14,11.233V3.95ZM12.715,4.4,8.487,7.02,3.565,4.272,8.144,1.963ZM3,5.1,8,7.894v5.7L3,10.473Zm6,8.434V7.878L13,5.4v5.318Z"/></g></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-fg{fill:#2a292c;}.icon-vs-action-purple{fill:#b180d7;}</style></defs><title>Method_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline"><path class="icon-vs-out" d="M15,3.349v8.4L8.975,16h-.9L1,11.582V3.327L7.6,0H8.713Z"/></g><g id="iconFg"><path class="icon-vs-fg" d="M12.715,4.4,8.487,7.02,3.565,4.272,8.144,1.963ZM3,5.1,8,7.894v5.7L3,10.473Zm6,8.434V7.878L13,5.4v5.318Z"/></g><g id="iconBg"><path class="icon-vs-action-purple" d="M8.156.837,2,3.942v7.085L8.517,15.1,14,11.233V3.95ZM12.715,4.4,8.487,7.02,3.565,4.272,8.144,1.963ZM3,5.1,8,7.894v5.7L3,10.473Zm6,8.434V7.878L13,5.4v5.318Z"/></g></svg>

After

Width:  |  Height:  |  Size: 821 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9.26 11.984l.978-.021a.962.962 0 0 0 .09-.006c.011-.063.026-.179.026-.361V9.688c0-.679.185-1.257.53-1.707-.346-.452-.53-1.03-.53-1.705V4.35c0-.167-.021-.259-.034-.302L9.26 4.02V.973l1.011.011c2.167.024 3.409 1.156 3.409 3.105v1.962c0 .351.071.461.072.462l.936.06.053.927v1.936l-.936.061c-.076.016-.125.146-.125.424v2.017c0 .914-.332 3.043-3.408 3.078l-1.012.011v-3.043zm-3.521 3.032c-3.089-.035-3.422-2.164-3.422-3.078V9.921c0-.327-.066-.432-.067-.433l-.937-.06-.063-.929V6.563l.942-.06c.058 0 .125-.114.125-.452V4.09c0-1.949 1.248-3.081 3.422-3.105L6.75.973V4.02l-.975.023a.572.572 0 0 0-.093.01c.006.021-.019.115-.019.297v1.928c0 .675-.186 1.253-.534 1.705.348.45.534 1.028.534 1.707v1.907c0 .175.014.291.027.363.023.002 1.06.025 1.06.025v3.043l-1.011-.012z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M5.75 14.016c-1.623-.019-2.434-.711-2.434-2.078V9.921c0-.902-.355-1.376-1.066-1.422v-.998c.711-.045 1.066-.529 1.066-1.449V4.09c0-1.385.811-2.087 2.434-2.105v1.06c-.725.017-1.087.453-1.087 1.305v1.928c0 .92-.454 1.488-1.36 1.702V8c.907.201 1.36.763 1.36 1.688v1.907c0 .488.081.835.243 1.042.162.208.443.316.844.325v1.054zm7.99-5.517c-.706.045-1.06.52-1.06 1.422v2.017c0 1.367-.807 2.06-2.42 2.078v-1.053c.396-.009.678-.118.844-.328.167-.21.25-.556.25-1.039V9.688c0-.925.449-1.488 1.347-1.688v-.021c-.898-.214-1.347-.782-1.347-1.702V4.35c0-.852-.364-1.288-1.094-1.306v-1.06c1.613.018 2.42.72 2.42 2.105v1.962c0 .92.354 1.404 1.06 1.449v.999z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9.26 11.984l.978-.021a.962.962 0 0 0 .09-.006c.011-.063.026-.179.026-.361V9.688c0-.679.185-1.257.53-1.707-.346-.452-.53-1.03-.53-1.705V4.35c0-.167-.021-.259-.034-.302L9.26 4.02V.973l1.011.011c2.167.024 3.409 1.156 3.409 3.105v1.962c0 .351.071.461.072.462l.936.06.053.927v1.936l-.936.061c-.076.016-.125.146-.125.424v2.017c0 .914-.332 3.043-3.408 3.078l-1.012.011v-3.043zm-3.521 3.032c-3.089-.035-3.422-2.164-3.422-3.078V9.921c0-.327-.066-.432-.067-.433l-.937-.06-.063-.929V6.563l.942-.06c.058 0 .125-.114.125-.452V4.09c0-1.949 1.248-3.081 3.422-3.105L6.75.973V4.02l-.975.023a.572.572 0 0 0-.093.01c.006.021-.019.115-.019.297v1.928c0 .675-.186 1.253-.534 1.705.348.45.534 1.028.534 1.707v1.907c0 .175.014.291.027.363.023.002 1.06.025 1.06.025v3.043l-1.011-.012z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M5.75 14.016c-1.623-.019-2.434-.711-2.434-2.078V9.921c0-.902-.355-1.376-1.066-1.422v-.998c.711-.045 1.066-.529 1.066-1.449V4.09c0-1.385.811-2.087 2.434-2.105v1.06c-.725.017-1.087.453-1.087 1.305v1.928c0 .92-.454 1.488-1.36 1.702V8c.907.201 1.36.763 1.36 1.688v1.907c0 .488.081.835.243 1.042.162.208.443.316.844.325v1.054zm7.99-5.517c-.706.045-1.06.52-1.06 1.422v2.017c0 1.367-.807 2.06-2.42 2.078v-1.053c.396-.009.678-.118.844-.328.167-.21.25-.556.25-1.039V9.688c0-.925.449-1.488 1.347-1.688v-.021c-.898-.214-1.347-.782-1.347-1.702V4.35c0-.852-.364-1.288-1.094-1.306v-1.06c1.613.018 2.42.72 2.42 2.105v1.962c0 .92.354 1.404 1.06 1.449v.999z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9 15v-3H7v3H4v-3H1V9h3V7H1V4h3V1h3v3h2V1h3v3h3v3h-3v2h3v3h-3v3H9z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M14 6V5h-3V2h-1v3H6V2H5v3H2v1h3v4H2v1h3v3h1v-3h4v3h1v-3h3v-1h-3V6h3zm-4 4H6V6h4v4z"/></g></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9 15v-3H7v3H4v-3H1V9h3V7H1V4h3V1h3v3h2V1h3v3h3v3h-3v2h3v3h-3v3H9z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M14 6V5h-3V2h-1v3H6V2H5v3H2v1h3v4H2v1h3v3h1v-3h4v3h1v-3h3v-1h-3V6h3zm-4 4H6V6h4v4z"/></g></svg>

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-fg{fill:#f0eff1}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 16H0V0h16v16z" id="outline" style="display: none;"/><path class="icon-vs-action-blue" d="M1 1v14h14V1H1zm6 12H3v-1h4v1zm0-3H3V9h4v1zm0-5H5v2H4V5H2V4h2V2h1v2h2v1zm3.281 8H8.719l3-4h1.563l-3.001 4zM14 5H9V4h5v1z" id="iconBg"/><path class="icon-vs-fg" d="M7 5H5v2H4V5H2V4h2V2h1v2h2v1zm7-1H9v1h5V4zM7 9H3v1h4V9zm0 3H3v1h4v-1zm3.281 1l3-4h-1.563l-3 4h1.563z" id="iconFg" style="display: none;"/></svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-fg{fill:#2b282e}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M16 16H0V0h16v16z" id="outline" style="display: none;"/><path class="icon-vs-action-blue" d="M1 1v14h14V1H1zm6 12H3v-1h4v1zm0-3H3V9h4v1zm0-5H5v2H4V5H2V4h2V2h1v2h2v1zm3.281 8H8.719l3-4h1.563l-3.001 4zM14 5H9V4h5v1z" id="iconBg"/><path class="icon-vs-fg" d="M7 5H5v2H4V5H2V4h2V2h1v2h2v1zm7-1H9v1h5V4zM7 9H3v1h4V9zm0 3H3v1h4v-1zm3.281 1l3-4h-1.563l-3 4h1.563z" id="iconFg" style="display: none;"/></svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#f6f6f6;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#424242;}</style></defs><title>Property_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline"><path class="icon-vs-out" d="M16,5.5a5.46,5.46,0,0,1-6.307,5.434l-.078-.012a5.439,5.439,0,0,1-.811-.191L4.268,15.268A2.5,2.5,0,0,1,.732,11.732L5.269,7.2a5.452,5.452,0,0,1-.191-.812c0-.025-.008-.051-.012-.077A5.5,5.5,0,1,1,16,5.5Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M15,5.5A4.474,4.474,0,0,1,8.571,9.55l-5.01,5.01a1.5,1.5,0,0,1-2.122-2.12L6.45,7.429A4.474,4.474,0,0,1,12.429,1.45L9.636,4.243l2.121,2.121L14.55,3.571A4.462,4.462,0,0,1,15,5.5Z"/></g></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.icon-canvas-transparent,.icon-vs-out{fill:#252526;}.icon-canvas-transparent{opacity:0;}.icon-vs-bg{fill:#c5c5c5;}</style></defs><title>Property_16x</title><g id="canvas"><path class="icon-canvas-transparent" d="M16,16H0V0H16Z"/></g><g id="outline"><path class="icon-vs-out" d="M16,5.5a5.46,5.46,0,0,1-6.307,5.434l-.078-.012a5.439,5.439,0,0,1-.811-.191L4.268,15.268A2.5,2.5,0,0,1,.732,11.732L5.269,7.2a5.452,5.452,0,0,1-.191-.812c0-.025-.008-.051-.012-.077A5.5,5.5,0,1,1,16,5.5Z"/></g><g id="iconBg"><path class="icon-vs-bg" d="M15,5.5A4.474,4.474,0,0,1,8.571,9.55l-5.01,5.01a1.5,1.5,0,0,1-2.122-2.12L6.45,7.429A4.474,4.474,0,0,1,12.429,1.45L9.636,4.243l2.121,2.121L14.55,3.571A4.462,4.462,0,0,1,15,5.5Z"/></g></svg>

After

Width:  |  Height:  |  Size: 789 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#f6f6f6}.st2{fill:#424242}.st3{fill:none}.st4{fill:#f0eff1}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M2 0h13v15H2z"/></g><g id="icon_x5F_bg"><path class="st2" d="M7 13h1v1H7zM5 13h1v1H5zM3 13h1v1H3zM9 13h1v1H9zM11 13h1v1h-1zM13 13h1v1h-1zM3 1v11h1V2h9v10h1V1z"/></g><g id="icon_x5F_fg"><path class="st3" d="M13 2H4h9z"/><path class="st4" d="M4 2h9v10H4z"/></g></svg>

After

Width:  |  Height:  |  Size: 503 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.st0{opacity:0}.st0,.st1{fill:#252526}.st2{fill:#c5c5c5}.st3{fill:none}.st4{fill:#2a292c}</style><g id="outline"><path class="st0" d="M0 0h16v16H0z"/><path class="st1" d="M2 0h13v15H2z"/></g><g id="icon_x5F_bg"><path class="st2" d="M7 13h1v1H7zM5 13h1v1H5zM3 13h1v1H3zM9 13h1v1H9zM11 13h1v1h-1zM13 13h1v1h-1zM3 1v11h1V2h9v10h1V1z"/></g><g id="icon_x5F_fg"><path class="st3" d="M13 2H4h9z"/><path class="st4" d="M4 2h9v10H4z"/></g></svg>

After

Width:  |  Height:  |  Size: 503 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0z" id="canvas"/><path class="icon-vs-out" d="M15.256 5.539a3.579 3.579 0 0 0-1.501-.306c-1.014 0-1.85.328-2.482.974-.084.086-.149.185-.222.278-.057-.091-.109-.186-.175-.27-.512-.642-1.243-.982-2.117-.982-.189 0-.372.016-.547.048V3.176h-3.02v2.736c-.463-.438-1.123-.68-1.949-.68-.8 0-1.524.189-2.153.562l-.49.291v2.081c-.288.393-.44.887-.44 1.464 0 .667.236 1.246.684 1.676.442.423 1.024.638 1.731.638.265 0 .517-.036.753-.105H7.8c.237.07.49.105.755.105.928 0 1.709-.365 2.258-1.055.052-.066.091-.142.138-.212.087.117.171.236.275.342.604.614 1.388.925 2.33.925.668 0 1.267-.15 1.779-.446l.5-.289V9.39l.004.003V5.806l-.583-.267zm-6.88 3.59l-.123-.013c-.024-.026-.051-.055-.051-.159v-.558c0-.21.063-.284.09-.315.021-.024.033-.038.119-.038l.033-.003c.02.025.084.128.084.418-.001.418-.091.587-.152.668zm5.834-.165c-.191.154-.326.171-.411.171-.229 0-.293-.066-.322-.097-.073-.076-.11-.214-.11-.411 0-.31.103-.418.14-.458.057-.061.134-.125.332-.125.086 0 .221.016.403.152l.479.358-.511.41z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M3.243 6.233c-.621 0-1.169.141-1.644.422v.892c.431-.369.914-.554 1.45-.554.609 0 .914.321.914.962l-1.336.189c-.978.141-1.467.636-1.467 1.486 0 .396.125.713.375.954.251.24.597.36 1.04.36.601 0 1.056-.27 1.367-.809h.018v.703h.989V7.911c-.001-1.119-.57-1.678-1.706-1.678zm.72 2.786c0 .331-.102.606-.305.824a1.011 1.011 0 0 1-.771.327c-.229 0-.411-.061-.547-.183a.595.595 0 0 1-.205-.467c0-.261.074-.443.222-.547.147-.104.368-.175.661-.213l.945-.132v.391zM8.758 6.233c-.671 0-1.181.299-1.529.896h-.018V4.176h-1.02v6.662h1.02v-.65h.018c.299.504.741.756 1.327.756.624 0 1.116-.227 1.476-.679.361-.453.541-1.056.541-1.809 0-.677-.16-1.216-.48-1.619s-.767-.604-1.335-.604zm.445 3.46c-.214.294-.511.441-.889.441-.322 0-.588-.114-.798-.343s-.314-.506-.314-.834v-.558c0-.387.11-.709.332-.967s.514-.387.877-.387c.343 0 .615.125.816.375.199.251.301.597.301 1.04-.001.528-.108.939-.325 1.233zM13.838 7.046c.354 0 .688.117 1.002.352v-.95a2.572 2.572 0 0 0-1.085-.215c-.738 0-1.328.225-1.769.675-.441.45-.662 1.045-.662 1.786 0 .665.205 1.206.615 1.624.41.417.949.626 1.617.626.492 0 .919-.104 1.279-.312v-.888c-.325.261-.671.391-1.037.391-.437 0-.784-.135-1.044-.404-.259-.27-.389-.637-.389-1.103 0-.472.138-.853.413-1.145.277-.291.63-.437 1.06-.437z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#252526}.icon-vs-out{fill:#252526}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M0 0h16v16H0z" id="canvas"/><path class="icon-vs-out" d="M15.256 5.539a3.579 3.579 0 0 0-1.501-.306c-1.014 0-1.85.328-2.482.974-.084.086-.149.185-.222.278-.057-.091-.109-.186-.175-.27-.512-.642-1.243-.982-2.117-.982-.189 0-.372.016-.547.048V3.176h-3.02v2.736c-.463-.438-1.123-.68-1.949-.68-.8 0-1.524.189-2.153.562l-.49.291v2.081c-.288.393-.44.887-.44 1.464 0 .667.236 1.246.684 1.676.442.423 1.024.638 1.731.638.265 0 .517-.036.753-.105H7.8c.237.07.49.105.755.105.928 0 1.709-.365 2.258-1.055.052-.066.091-.142.138-.212.087.117.171.236.275.342.604.614 1.388.925 2.33.925.668 0 1.267-.15 1.779-.446l.5-.289V9.39l.004.003V5.806l-.583-.267zm-6.88 3.59l-.123-.013c-.024-.026-.051-.055-.051-.159v-.558c0-.21.063-.284.09-.315.021-.024.033-.038.119-.038l.033-.003c.02.025.084.128.084.418-.001.418-.091.587-.152.668zm5.834-.165c-.191.154-.326.171-.411.171-.229 0-.293-.066-.322-.097-.073-.076-.11-.214-.11-.411 0-.31.103-.418.14-.458.057-.061.134-.125.332-.125.086 0 .221.016.403.152l.479.358-.511.41z" id="outline"/><g id="iconBg"><path class="icon-vs-bg" d="M3.243 6.233c-.621 0-1.169.141-1.644.422v.892c.431-.369.914-.554 1.45-.554.609 0 .914.321.914.962l-1.336.189c-.978.141-1.467.636-1.467 1.486 0 .396.125.713.375.954.251.24.597.36 1.04.36.601 0 1.056-.27 1.367-.809h.018v.703h.989V7.911c-.001-1.119-.57-1.678-1.706-1.678zm.72 2.786c0 .331-.102.606-.305.824a1.011 1.011 0 0 1-.771.327c-.229 0-.411-.061-.547-.183a.595.595 0 0 1-.205-.467c0-.261.074-.443.222-.547.147-.104.368-.175.661-.213l.945-.132v.391zM8.758 6.233c-.671 0-1.181.299-1.529.896h-.018V4.176h-1.02v6.662h1.02v-.65h.018c.299.504.741.756 1.327.756.624 0 1.116-.227 1.476-.679.361-.453.541-1.056.541-1.809 0-.677-.16-1.216-.48-1.619s-.767-.604-1.335-.604zm.445 3.46c-.214.294-.511.441-.889.441-.322 0-.588-.114-.798-.343s-.314-.506-.314-.834v-.558c0-.387.11-.709.332-.967s.514-.387.877-.387c.343 0 .615.125.816.375.199.251.301.597.301 1.04-.001.528-.108.939-.325 1.233zM13.838 7.046c.354 0 .688.117 1.002.352v-.95a2.572 2.572 0 0 0-1.085-.215c-.738 0-1.328.225-1.769.675-.441.45-.662 1.045-.662 1.786 0 .665.205 1.206.615 1.624.41.417.949.626 1.617.626.492 0 .919-.104 1.279-.312v-.888c-.325.261-.671.391-1.037.391-.437 0-.784-.135-1.044-.404-.259-.27-.389-.637-.389-1.103 0-.472.138-.853.413-1.145.277-.291.63-.437 1.06-.437z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-action-blue{fill:#00539c}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9 14V8H7v6H1V2h14v12H9z" id="outline" style="display: none;"/><path class="icon-vs-action-blue" d="M10 9h4v4h-4V9zm-8 4h4V9H2v4zM2 3v4h12V3H2z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-action-blue{fill:#75beff}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M9 14V8H7v6H1V2h14v12H9z" id="outline" style="display: none;"/><path class="icon-vs-action-blue" d="M10 9h4v4h-4V9zm-8 4h4V9H2v4zM2 3v4h12V3H2z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#f6f6f6}.icon-vs-out{fill:#f6f6f6}.icon-vs-bg{fill:#424242}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M10.702 10.5l2-2-2-2 .5-.5H10v5h1v3H5v-3h1V6H4.798l.5.5-2 2 2 2L3 12.797l-3-3V7.201l3-3V2h10v2.201l3 3v2.596l-3 3-2.298-2.297z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M4 3h8v2h-1v-.5c0-.277-.224-.5-.5-.5H9v7.5c0 .275.224.5.5.5h.5v1H6v-1h.5a.5.5 0 0 0 .5-.5V4H5.5a.5.5 0 0 0-.5.5V5H4V3zM3 5.615L.116 8.5 3 11.383l.884-.883-2-2 2-2L3 5.615zm10 0l-.884.885 2 2-2 2 .884.883L15.884 8.5 13 5.615z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 714 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><style>.icon-canvas-transparent{opacity:0;fill:#2d2d30}.icon-vs-out{fill:#2d2d30}.icon-vs-bg{fill:#c5c5c5}</style><path class="icon-canvas-transparent" d="M16 16H0V0h16v16z" id="canvas"/><path class="icon-vs-out" d="M10.702 10.5l2-2-2-2 .5-.5H10v5h1v3H5v-3h1V6H4.798l.5.5-2 2 2 2L3 12.797l-3-3V7.201l3-3V2h10v2.201l3 3v2.596l-3 3-2.298-2.297z" id="outline" style="display: none;"/><path class="icon-vs-bg" d="M4 3h8v2h-1v-.5c0-.277-.224-.5-.5-.5H9v7.5c0 .275.224.5.5.5h.5v1H6v-1h.5a.5.5 0 0 0 .5-.5V4H5.5a.5.5 0 0 0-.5.5V5H4V3zM3 5.615L.116 8.5 3 11.383l.884-.883-2-2 2-2L3 5.615zm10 0l-.884.885 2 2-2 2 .884.883L15.884 8.5 13 5.615z" id="iconBg"/></svg>

After

Width:  |  Height:  |  Size: 714 B

View File

@@ -0,0 +1,66 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-tree.focused .selected .outline-element-label, .monaco-tree.focused .selected .outline-element-decoration {
/* make sure selection color wins when a label is being selected */
color: inherit !important;
}
.monaco-tree .outline-element {
display: flex;
flex: 1;
flex-flow: row nowrap;
align-items: center;
}
.monaco-tree .outline-element .outline-element-icon {
padding-right: 3px;
}
/* .monaco-tree.no-icons .outline-element .outline-element-icon {
display: none;
} */
.monaco-tree .outline-element .outline-element-label {
text-overflow: ellipsis;
overflow: hidden;
color: var(--outline-element-color);
}
.monaco-tree .outline-element .outline-element-label .monaco-highlighted-label .highlight {
font-weight: bold;
}
.monaco-tree .outline-element .outline-element-detail {
visibility: hidden;
flex: 1;
flex-basis: 10%;
opacity: 0.8;
overflow: hidden;
text-overflow: ellipsis;
font-size: 90%;
padding-left: 4px;
padding-top: 3px;
}
.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail {
visibility: inherit;
}
.monaco-tree .outline-element .outline-element-decoration {
opacity: 0.75;
font-size: 90%;
font-weight: 600;
padding: 0 12px 0 5px;
margin-left: auto;
text-align: center;
color: var(--outline-element-color);
}
.monaco-tree .outline-element .outline-element-decoration.bubble {
font-family: octicons;
font-size: 14px;
opacity: 0.4;
}

View File

@@ -0,0 +1,276 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-workbench .symbol-icon {
display: inline-block;
height: 14px;
width: 16px;
min-height: 14px;
min-width: 16px;
}
/* default icons */
.monaco-workbench .symbol-icon {
background-image: url('Field_16x.svg');
background-repeat: no-repeat;
}
.vs-dark .monaco-workbench .symbol-icon,
.hc-black .monaco-workbench .symbol-icon {
background-image: url('Field_16x_darkp.svg');
}
/* constant */
.monaco-workbench .symbol-icon.constant {
background-image: url('Constant_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.constant,
.hc-black .monaco-workbench .symbol-icon.constant {
background-image: url('Constant_16x_inverse.svg');
}
/* enum */
.monaco-workbench .symbol-icon.enum {
background-image: url('Enumerator_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.enum,
.hc-black .monaco-workbench .symbol-icon.enum {
background-image: url('Enumerator_inverse_16x.svg');
}
/* enum-member */
.monaco-workbench .symbol-icon.enum-member {
background-image: url('EnumItem_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.enum-member,
.hc-black .monaco-workbench .symbol-icon.enum-member {
background-image: url('EnumItem_inverse_16x.svg');
}
/* struct */
.monaco-workbench .symbol-icon.struct {
background-image: url('Structure_16x_vscode.svg');
}
.vs-dark .monaco-workbench .symbol-icon.struct,
.hc-black .monaco-workbench .symbol-icon.struct {
background-image: url('Structure_16x_vscode_inverse.svg');
}
/* event */
.monaco-workbench .symbol-icon.event {
background-image: url('Event_16x_vscode.svg');
}
.vs-dark .monaco-workbench .symbol-icon.event,
.hc-black .monaco-workbench .symbol-icon.event {
background-image: url('Event_16x_vscode_inverse.svg');
}
/* operator */
.monaco-workbench .symbol-icon.operator {
background-image: url('Operator_16x_vscode.svg');
}
.vs-dark .monaco-workbench .symbol-icon.operator,
.hc-black .monaco-workbench .symbol-icon.operator {
background-image: url('Operator_16x_vscode_inverse.svg');
}
/* type paramter */
.monaco-workbench .symbol-icon.type-parameter {
background-image: url('Template_16x_vscode.svg');
}
.vs-dark .monaco-workbench .symbol-icon.type-parameter,
.hc-black .monaco-workbench .symbol-icon.type-parameter {
background-image: url('Template_16x_vscode_inverse.svg');
}
/* boolean, null */
.monaco-workbench .symbol-icon.boolean {
background-image: url('BooleanData_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.boolean,
.hc-black .monaco-workbench .symbol-icon.boolean {
background-image: url('BooleanData_16x_darkp.svg');
}
/* null */
.monaco-workbench .symbol-icon.null {
background-image: url('BooleanData_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.null,
.hc-black .monaco-workbench .symbol-icon.null {
background-image: url('BooleanData_16x_darkp.svg');
}
/* class */
.monaco-workbench .symbol-icon.class {
background-image: url('Class_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.class,
.hc-black .monaco-workbench .symbol-icon.class {
background-image: url('Class_16x_darkp.svg');
}
/* constructor */
.monaco-workbench .symbol-icon.constructor {
background-image: url('Method_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.constructor,
.hc-black .monaco-workbench .symbol-icon.constructor {
background-image: url('Method_16x_darkp.svg');
}
/* file */
.monaco-workbench .symbol-icon.file {
background-image: url('Document_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.file,
.hc-black .monaco-workbench .symbol-icon.file {
background-image: url('Document_16x_darkp.svg');
}
/* field */
.monaco-workbench .symbol-icon.field {
background-image: url('Field_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.field,
.hc-black .monaco-workbench .symbol-icon.field {
background-image: url('Field_16x_darkp.svg');
}
/* variable */
.monaco-workbench .symbol-icon.variable {
background-image: url('LocalVariable_16x_vscode.svg');
}
.vs-dark .monaco-workbench .symbol-icon.variable,
.hc-black .monaco-workbench .symbol-icon.variable {
background-image: url('LocalVariable_16x_vscode_inverse.svg');
}
/* array */
.monaco-workbench .symbol-icon.array {
background-image: url('Indexer_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.array,
.hc-black .monaco-workbench .symbol-icon.array {
background-image: url('Indexer_16x_darkp.svg');
}
/* keyword */
/* todo@joh not used? */
.monaco-workbench .symbol-icon.keyword {
background-image: url('IntelliSenseKeyword_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.keyword,
.hc-black .monaco-workbench .symbol-icon.keyword {
background-image: url('IntelliSenseKeyword_16x_darkp.svg');
}
/* interface */
.monaco-workbench .symbol-icon.interface {
background-image: url('Interface_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.interface,
.hc-black .monaco-workbench .symbol-icon.interface {
background-image: url('Interface_16x_darkp.svg');
}
/* method */
.monaco-workbench .symbol-icon.method {
background-image: url('Method_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.method,
.hc-black .monaco-workbench .symbol-icon.method {
background-image: url('Method_16x_darkp.svg');
}
/* function */
.monaco-workbench .symbol-icon.function {
background-image: url('Method_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.function,
.hc-black .monaco-workbench .symbol-icon.function {
background-image: url('Method_16x_darkp.svg');
}
/* object */
.monaco-workbench .symbol-icon.object {
background-image: url('Namespace_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.object,
.hc-black .monaco-workbench .symbol-icon.object {
background-image: url('Namespace_16x_darkp.svg');
}
/* namespace */
.monaco-workbench .symbol-icon.namespace {
background-image: url('Namespace_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.namespace,
.hc-black .monaco-workbench .symbol-icon.namespace {
background-image: url('Namespace_16x_darkp.svg');
}
/* package */
.monaco-workbench .symbol-icon.package {
background-image: url('Namespace_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.package,
.hc-black .monaco-workbench .symbol-icon.package {
background-image: url('Namespace_16x_darkp.svg');
}
/* module */
.monaco-workbench .symbol-icon.module {
background-image: url('Namespace_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.module,
.hc-black .monaco-workbench .symbol-icon.module {
background-image: url('Namespace_16x_darkp.svg');
}
/* number */
.monaco-workbench .symbol-icon.number {
background-image: url('Numeric_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.number,
.hc-black .monaco-workbench .symbol-icon.number {
background-image: url('Numeric_16x_darkp.svg');
}
/* property */
.monaco-workbench .symbol-icon.property {
background-image: url('Property_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.property,
.hc-black .monaco-workbench .symbol-icon.property {
background-image: url('Property_16x_darkp.svg');
}
/* snippet */
/* todo@joh unused? */
.monaco-workbench .symbol-icon.snippet {
background-image: url('Snippet_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.snippet,
.hc-black .monaco-workbench .symbol-icon.snippet {
background-image: url('Snippet_16x_darkp.svg');
}
/* string */
.monaco-workbench .symbol-icon.string {
background-image: url('String_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.string,
.hc-black .monaco-workbench .symbol-icon.string {
background-image: url('String_16x_darkp.svg');
}
/* key */
.monaco-workbench .symbol-icon.key {
background-image: url('String_16x.svg');
}
.vs-dark .monaco-workbench .symbol-icon.key,
.hc-black .monaco-workbench .symbol-icon.key {
background-image: url('String_16x_darkp.svg');
}

View File

@@ -0,0 +1,441 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { DocumentSymbolProviderRegistry, DocumentSymbolProvider, DocumentSymbol } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { fuzzyScore, FuzzyScore } from 'vs/base/common/filters';
import { IPosition } from 'vs/editor/common/core/position';
import { Range, IRange } from 'vs/editor/common/core/range';
import { first, size, forEach } from 'vs/base/common/collections';
import { isFalsyOrEmpty, binarySearch, coalesce } from 'vs/base/common/arrays';
import { commonPrefixLength } from 'vs/base/common/strings';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { LRUCache } from 'vs/base/common/map';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
export abstract class TreeElement {
abstract id: string;
abstract children: { [id: string]: TreeElement };
abstract parent: TreeElement;
abstract adopt(newParent: TreeElement): TreeElement;
remove(): void {
delete this.parent.children[this.id];
}
static findId(candidate: DocumentSymbol | string, container: TreeElement): string {
// complex id-computation which contains the origin/extension,
// the parent path, and some dedupe logic when names collide
let candidateId: string;
if (typeof candidate === 'string') {
candidateId = `${container.id}/${candidate}`;
} else {
candidateId = `${container.id}/${candidate.name}`;
if (container.children[candidateId] !== void 0) {
candidateId = `${container.id}/${candidate.name}_${candidate.range.startLineNumber}_${candidate.range.startColumn}`;
}
}
let id = candidateId;
for (let i = 0; container.children[id] !== void 0; i++) {
id = `${candidateId}_${i}`;
}
return id;
}
static getElementById(id: string, element: TreeElement): TreeElement {
if (!id) {
return undefined;
}
let len = commonPrefixLength(id, element.id);
if (len === id.length) {
return element;
}
if (len < element.id.length) {
return undefined;
}
for (const key in element.children) {
let candidate = TreeElement.getElementById(id, element.children[key]);
if (candidate) {
return candidate;
}
}
return undefined;
}
static size(element: TreeElement): number {
let res = 1;
for (const key in element.children) {
res += TreeElement.size(element.children[key]);
}
return res;
}
static empty(element: TreeElement): boolean {
for (const _key in element.children) {
return false;
}
return true;
}
}
export class OutlineElement extends TreeElement {
children: { [id: string]: OutlineElement; } = Object.create(null);
score: FuzzyScore = [0, []];
marker: { count: number, topSev: MarkerSeverity };
constructor(
readonly id: string,
public parent: OutlineModel | OutlineGroup | OutlineElement,
readonly symbol: DocumentSymbol
) {
super();
}
adopt(parent: OutlineModel | OutlineGroup | OutlineElement): OutlineElement {
let res = new OutlineElement(this.id, parent, this.symbol);
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
return res;
}
}
export class OutlineGroup extends TreeElement {
children: { [id: string]: OutlineElement; } = Object.create(null);
constructor(
readonly id: string,
public parent: OutlineModel,
readonly provider: DocumentSymbolProvider,
readonly providerIndex: number,
) {
super();
}
adopt(parent: OutlineModel): OutlineGroup {
let res = new OutlineGroup(this.id, parent, this.provider, this.providerIndex);
forEach(this.children, entry => res.children[entry.key] = entry.value.adopt(res));
return res;
}
updateMatches(pattern: string, topMatch: OutlineElement): OutlineElement {
for (const key in this.children) {
topMatch = this._updateMatches(pattern, this.children[key], topMatch);
}
return topMatch;
}
private _updateMatches(pattern: string, item: OutlineElement, topMatch: OutlineElement): OutlineElement {
item.score = fuzzyScore(pattern, item.symbol.name, undefined, true);
if (item.score && (!topMatch || item.score[0] > topMatch.score[0])) {
topMatch = item;
}
for (const key in item.children) {
let child = item.children[key];
topMatch = this._updateMatches(pattern, child, topMatch);
if (!item.score && child.score) {
// don't filter parents with unfiltered children
item.score = [0, []];
}
}
return topMatch;
}
getItemEnclosingPosition(position: IPosition): OutlineElement {
return this._getItemEnclosingPosition(position, this.children);
}
private _getItemEnclosingPosition(position: IPosition, children: { [id: string]: OutlineElement }): OutlineElement {
for (let key in children) {
let item = children[key];
if (!Range.containsPosition(item.symbol.range, position)) {
continue;
}
return this._getItemEnclosingPosition(position, item.children) || item;
}
return undefined;
}
updateMarker(marker: IMarker[]): void {
for (const key in this.children) {
this._updateMarker(marker, this.children[key]);
}
}
private _updateMarker(markers: IMarker[], item: OutlineElement): void {
item.marker = undefined;
// find the proper start index to check for item/marker overlap.
let idx = binarySearch<IRange>(markers, item.symbol.range, Range.compareRangesUsingStarts);
let start: number;
if (idx < 0) {
start = ~idx;
if (start > 0 && Range.areIntersecting(markers[start - 1], item.symbol.range)) {
start -= 1;
}
} else {
start = idx;
}
let myMarkers: IMarker[] = [];
let myTopSev: MarkerSeverity;
for (; start < markers.length && Range.areIntersecting(item.symbol.range, markers[start]); start++) {
// remove markers intersecting with this outline element
// and store them in a 'private' array.
let marker = markers[start];
myMarkers.push(marker);
markers[start] = undefined;
if (!myTopSev || marker.severity > myTopSev) {
myTopSev = marker.severity;
}
}
// Recurse into children and let them match markers that have matched
// this outline element. This might remove markers from this element and
// therefore we remember that we have had markers. That allows us to render
// the dot, saying 'this element has children with markers'
for (const key in item.children) {
this._updateMarker(myMarkers, item.children[key]);
}
if (myTopSev) {
item.marker = {
count: myMarkers.length,
topSev: myTopSev
};
}
coalesce(markers, true);
}
}
export class OutlineModel extends TreeElement {
private static readonly _requests = new LRUCache<string, { promiseCnt: number, source: CancellationTokenSource, promise: Promise<any>, model: OutlineModel }>(9, .75);
private static readonly _keys = new class {
private _counter = 1;
private _data = new WeakMap<DocumentSymbolProvider, number>();
for(textModel: ITextModel): string {
return `${textModel.id}/${textModel.getVersionId()}/${this._hash(DocumentSymbolProviderRegistry.all(textModel))}`;
}
private _hash(providers: DocumentSymbolProvider[]): string {
let result = '';
for (const provider of providers) {
let n = this._data.get(provider);
if (typeof n === 'undefined') {
n = this._counter++;
this._data.set(provider, n);
}
result += n;
}
return result;
}
};
static create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
let key = this._keys.for(textModel);
let data = OutlineModel._requests.get(key);
if (!data) {
let source = new CancellationTokenSource();
data = {
promiseCnt: 0,
source,
promise: OutlineModel._create(textModel, source.token),
model: undefined,
};
OutlineModel._requests.set(key, data);
}
if (data.model) {
// resolved -> return data
return Promise.resolve(data.model);
}
// increase usage counter
data.promiseCnt += 1;
token.onCancellationRequested(() => {
// last -> cancel provider request, remove cached promise
if (--data.promiseCnt === 0) {
data.source.cancel();
OutlineModel._requests.delete(key);
}
});
return new Promise((resolve, reject) => {
data.promise.then(model => {
data.model = model;
resolve(model);
}, err => {
OutlineModel._requests.delete(key);
reject(err);
});
});
}
static _create(textModel: ITextModel, token: CancellationToken): Promise<OutlineModel> {
let result = new OutlineModel(textModel);
let promises = DocumentSymbolProviderRegistry.ordered(textModel).map((provider, index) => {
let id = TreeElement.findId(`provider_${index}`, result);
let group = new OutlineGroup(id, result, provider, index);
return Promise.resolve(provider.provideDocumentSymbols(result.textModel, token)).then(result => {
if (!isFalsyOrEmpty(result)) {
for (const info of result) {
OutlineModel._makeOutlineElement(info, group);
}
}
return group;
}, err => {
onUnexpectedExternalError(err);
return group;
}).then(group => {
if (!TreeElement.empty(group)) {
result._groups[id] = group;
} else {
group.remove();
}
});
});
return Promise.all(promises).then(() => result._compact());
}
private static _makeOutlineElement(info: DocumentSymbol, container: OutlineGroup | OutlineElement): void {
let id = TreeElement.findId(info, container);
let res = new OutlineElement(id, container, info);
if (info.children) {
for (const childInfo of info.children) {
OutlineModel._makeOutlineElement(childInfo, res);
}
}
container.children[res.id] = res;
}
static get(element: TreeElement): OutlineModel {
while (element) {
if (element instanceof OutlineModel) {
return element;
}
element = element.parent;
}
return undefined;
}
readonly id = 'root';
readonly parent = undefined;
protected _groups: { [id: string]: OutlineGroup; } = Object.create(null);
children: { [id: string]: OutlineGroup | OutlineElement; } = Object.create(null);
protected constructor(readonly textModel: ITextModel) {
super();
}
adopt(): OutlineModel {
let res = new OutlineModel(this.textModel);
forEach(this._groups, entry => res._groups[entry.key] = entry.value.adopt(res));
return res._compact();
}
private _compact(): this {
let count = 0;
for (const key in this._groups) {
let group = this._groups[key];
if (first(group.children) === undefined) { // empty
delete this._groups[key];
} else {
count += 1;
}
}
if (count !== 1) {
//
this.children = this._groups;
} else {
// adopt all elements of the first group
let group = first(this._groups);
for (let key in group.children) {
let child = group.children[key];
child.parent = this;
this.children[child.id] = child;
}
}
return this;
}
merge(other: OutlineModel): boolean {
if (this.textModel.uri.toString() !== other.textModel.uri.toString()) {
return false;
}
if (size(this._groups) !== size(other._groups)) {
return false;
}
this._groups = other._groups;
this.children = other.children;
return true;
}
updateMatches(pattern: string): OutlineElement {
let topMatch: OutlineElement;
for (const key in this._groups) {
topMatch = this._groups[key].updateMatches(pattern, topMatch);
}
return topMatch;
}
getItemEnclosingPosition(position: IPosition, context?: OutlineElement): OutlineElement {
let preferredGroup: OutlineGroup;
if (context) {
let candidate = context.parent;
while (candidate && !preferredGroup) {
if (candidate instanceof OutlineGroup) {
preferredGroup = candidate;
}
candidate = candidate.parent;
}
}
let result: OutlineElement = undefined;
for (const key in this._groups) {
const group = this._groups[key];
result = group.getItemEnclosingPosition(position);
if (result && (!preferredGroup || preferredGroup === group)) {
break;
}
}
return result;
}
getItemById(id: string): TreeElement {
return TreeElement.getElementById(id, this);
}
updateMarker(marker: IMarker[]): void {
// sort markers by start range so that we can use
// outline element starts for quicker look up
marker.sort(Range.compareRangesUsingStarts);
for (const key in this._groups) {
this._groups[key].updateMarker(marker.slice(0));
}
}
}

View File

@@ -0,0 +1,318 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as dom from 'vs/base/browser/dom';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import { values } from 'vs/base/common/collections';
import { createMatches } from 'vs/base/common/filters';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/outlineTree';
import 'vs/css!./media/symbol-icons';
import { Range } from 'vs/editor/common/core/range';
import { SymbolKind, symbolKindToCssClass } from 'vs/editor/common/modes';
import { OutlineElement, OutlineGroup, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
import { MarkerSeverity } from 'vs/platform/markers/common/markers';
import { listErrorForeground, listWarningForeground } from 'vs/platform/theme/common/colorRegistry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export const enum OutlineItemCompareType {
ByPosition,
ByName,
ByKind
}
export class OutlineItemComparator implements ISorter {
constructor(
public type: OutlineItemCompareType = OutlineItemCompareType.ByPosition
) { }
compare(tree: ITree, a: OutlineGroup | OutlineElement, b: OutlineGroup | OutlineElement): number {
if (a instanceof OutlineGroup && b instanceof OutlineGroup) {
return a.providerIndex - b.providerIndex;
}
if (a instanceof OutlineElement && b instanceof OutlineElement) {
switch (this.type) {
case OutlineItemCompareType.ByKind:
return a.symbol.kind - b.symbol.kind;
case OutlineItemCompareType.ByName:
return a.symbol.name.localeCompare(b.symbol.name);
case OutlineItemCompareType.ByPosition:
default:
return Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range);
}
}
return 0;
}
}
export class OutlineItemFilter implements IFilter {
enabled: boolean = true;
isVisible(tree: ITree, element: OutlineElement | any): boolean {
if (!this.enabled) {
return true;
}
return !(element instanceof OutlineElement) || Boolean(element.score);
}
}
export class OutlineDataSource implements IDataSource {
// this is a workaround for the tree showing twisties for items
// with only filtered children
filterOnScore: boolean = true;
getId(tree: ITree, element: TreeElement): string {
return element ? element.id : 'empty';
}
hasChildren(tree: ITree, element: OutlineModel | OutlineGroup | OutlineElement): boolean {
if (!element) {
return false;
}
if (element instanceof OutlineModel) {
return true;
}
if (element instanceof OutlineElement && (this.filterOnScore && !element.score)) {
return false;
}
for (const id in element.children) {
if (!this.filterOnScore || element.children[id].score) {
return true;
}
}
return false;
}
getChildren(tree: ITree, element: TreeElement): TPromise<TreeElement[]> {
let res = values(element.children);
// console.log(element.id + ' with children ' + res.length);
return TPromise.wrap(res);
}
getParent(tree: ITree, element: TreeElement | any): TPromise<TreeElement> {
return TPromise.wrap(element && element.parent);
}
shouldAutoexpand(tree: ITree, element: TreeElement): boolean {
return element && (element instanceof OutlineModel || element.parent instanceof OutlineModel || element instanceof OutlineGroup || element.parent instanceof OutlineGroup);
}
}
export interface OutlineTemplate {
labelContainer: HTMLElement;
label: HighlightedLabel;
icon?: HTMLElement;
detail?: HTMLElement;
decoration?: HTMLElement;
}
export class OutlineRenderer implements IRenderer {
renderProblemColors = true;
renderProblemBadges = true;
constructor(
@IThemeService readonly _themeService: IThemeService,
@IConfigurationService readonly _configurationService: IConfigurationService
) {
//
}
getHeight(tree: ITree, element: any): number {
return 22;
}
getTemplateId(tree: ITree, element: OutlineGroup | OutlineElement): string {
return element instanceof OutlineGroup ? 'outline-group' : 'outline-element';
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): OutlineTemplate {
if (templateId === 'outline-element') {
const icon = dom.$('.outline-element-icon symbol-icon');
const labelContainer = dom.$('.outline-element-label');
const detail = dom.$('.outline-element-detail');
const decoration = dom.$('.outline-element-decoration');
dom.addClass(container, 'outline-element');
dom.append(container, icon, labelContainer, detail, decoration);
return { icon, labelContainer, label: new HighlightedLabel(labelContainer), detail, decoration };
}
if (templateId === 'outline-group') {
const labelContainer = dom.$('.outline-element-label');
dom.addClass(container, 'outline-element');
dom.append(container, labelContainer);
return { labelContainer, label: new HighlightedLabel(labelContainer) };
}
throw new Error(templateId);
}
renderElement(tree: ITree, element: OutlineGroup | OutlineElement, templateId: string, template: OutlineTemplate): void {
if (element instanceof OutlineElement) {
template.icon.className = `outline-element-icon ${symbolKindToCssClass(element.symbol.kind)}`;
template.label.set(element.symbol.name, element.score ? createMatches(element.score[1]) : undefined, localize('title.template', "{0} ({1})", element.symbol.name, OutlineRenderer._symbolKindNames[element.symbol.kind]));
template.detail.innerText = element.symbol.detail || '';
this._renderMarkerInfo(element, template);
}
if (element instanceof OutlineGroup) {
template.label.set(element.provider.displayName || localize('provider', "Outline Provider"));
}
}
private _renderMarkerInfo(element: OutlineElement, template: OutlineTemplate): void {
if (!element.marker) {
dom.hide(template.decoration);
template.labelContainer.style.removeProperty('--outline-element-color');
return;
}
const { count, topSev } = element.marker;
const color = this._themeService.getTheme().getColor(topSev === MarkerSeverity.Error ? listErrorForeground : listWarningForeground);
const cssColor = color ? color.toString() : 'inherit';
// color of the label
if (this.renderProblemColors) {
template.labelContainer.style.setProperty('--outline-element-color', cssColor);
} else {
template.labelContainer.style.removeProperty('--outline-element-color');
}
// badge with color/rollup
if (!this.renderProblemBadges) {
dom.hide(template.decoration);
} else if (count > 0) {
dom.show(template.decoration);
dom.removeClass(template.decoration, 'bubble');
template.decoration.innerText = count < 10 ? count.toString() : '+9';
template.decoration.title = count === 1 ? localize('1.problem', "1 problem in this element") : localize('N.problem', "{0} problems in this element", count);
template.decoration.style.setProperty('--outline-element-color', cssColor);
} else {
dom.show(template.decoration);
dom.addClass(template.decoration, 'bubble');
template.decoration.innerText = '\uf052';
template.decoration.title = localize('deep.problem', "Contains elements with problems");
template.decoration.style.setProperty('--outline-element-color', cssColor);
}
}
private static _symbolKindNames: { [symbol: number]: string } = {
[SymbolKind.Array]: localize('Array', "array"),
[SymbolKind.Boolean]: localize('Boolean', "boolean"),
[SymbolKind.Class]: localize('Class', "class"),
[SymbolKind.Constant]: localize('Constant', "constant"),
[SymbolKind.Constructor]: localize('Constructor', "constructor"),
[SymbolKind.Enum]: localize('Enum', "enumeration"),
[SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"),
[SymbolKind.Event]: localize('Event', "event"),
[SymbolKind.Field]: localize('Field', "field"),
[SymbolKind.File]: localize('File', "file"),
[SymbolKind.Function]: localize('Function', "function"),
[SymbolKind.Interface]: localize('Interface', "interface"),
[SymbolKind.Key]: localize('Key', "key"),
[SymbolKind.Method]: localize('Method', "method"),
[SymbolKind.Module]: localize('Module', "module"),
[SymbolKind.Namespace]: localize('Namespace', "namespace"),
[SymbolKind.Null]: localize('Null', "null"),
[SymbolKind.Number]: localize('Number', "number"),
[SymbolKind.Object]: localize('Object', "object"),
[SymbolKind.Operator]: localize('Operator', "operator"),
[SymbolKind.Package]: localize('Package', "package"),
[SymbolKind.Property]: localize('Property', "property"),
[SymbolKind.String]: localize('String', "string"),
[SymbolKind.Struct]: localize('Struct', "struct"),
[SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"),
[SymbolKind.Variable]: localize('Variable', "variable"),
};
disposeTemplate(tree: ITree, templateId: string, template: OutlineTemplate): void {
template.label.dispose();
}
}
export class OutlineTreeState {
readonly selected: string;
readonly focused: string;
readonly expanded: string[];
static capture(tree: ITree): OutlineTreeState {
// selection
let selected: string;
let element = tree.getSelection()[0];
if (element instanceof TreeElement) {
selected = element.id;
}
// focus
let focused: string;
element = tree.getFocus(true);
if (element instanceof TreeElement) {
focused = element.id;
}
// expansion
let expanded = new Array<string>();
let nav = tree.getNavigator();
while (nav.next()) {
let element = nav.current();
if (element instanceof TreeElement) {
if (tree.isExpanded(element)) {
expanded.push(element.id);
}
}
}
return { selected, focused, expanded };
}
static async restore(tree: ITree, state: OutlineTreeState, eventPayload: any): Promise<void> {
let model = <OutlineModel>tree.getInput();
if (!state || !(model instanceof OutlineModel)) {
return TPromise.as(undefined);
}
// expansion
let items: TreeElement[] = [];
for (const id of state.expanded) {
let item = model.getItemById(id);
if (item) {
items.push(item);
}
}
await tree.collapseAll(undefined);
await tree.expandAll(items);
// selection & focus
let selected = model.getItemById(state.selected);
let focused = model.getItemById(state.focused);
tree.setSelection([selected], eventPayload);
tree.setFocus(focused, eventPayload);
}
}
export class OutlineController extends WorkbenchTreeController {
protected shouldToggleExpansion(element: any, event: IMouseEvent, origin: string): boolean {
if (element instanceof OutlineElement) {
return this.isClickOnTwistie(event);
} else {
return super.shouldToggleExpansion(element, event, origin);
}
}
}

View File

@@ -0,0 +1,188 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { OutlineElement, OutlineGroup, OutlineModel } from '../outlineModel';
import { SymbolKind, DocumentSymbol, DocumentSymbolProviderRegistry } from 'vs/editor/common/modes';
import { Range } from 'vs/editor/common/core/range';
import { IMarker, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { TextModel } from 'vs/editor/common/model/textModel';
import URI from 'vs/base/common/uri';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
suite('OutlineModel', function () {
test('OutlineModel#create, cached', async function () {
let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo'));
let count = 0;
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
provideDocumentSymbols() {
count += 1;
return [];
}
});
await OutlineModel.create(model, CancellationToken.None);
assert.equal(count, 1);
// cached
await OutlineModel.create(model, CancellationToken.None);
assert.equal(count, 1);
// new version
model.applyEdits([{ text: 'XXX', range: new Range(1, 1, 1, 1) }]);
await OutlineModel.create(model, CancellationToken.None);
assert.equal(count, 2);
reg.dispose();
});
test('OutlineModel#create, cached/cancel', async function () {
let model = TextModel.createFromString('foo', undefined, undefined, URI.file('/fome/path.foo'));
let isCancelled = false;
let reg = DocumentSymbolProviderRegistry.register({ pattern: '**/path.foo' }, {
provideDocumentSymbols(d, token) {
return new Promise(resolve => {
token.onCancellationRequested(_ => {
isCancelled = true;
resolve(null);
});
});
}
});
assert.equal(isCancelled, false);
let s1 = new CancellationTokenSource();
OutlineModel.create(model, s1.token);
let s2 = new CancellationTokenSource();
OutlineModel.create(model, s2.token);
s1.cancel();
assert.equal(isCancelled, false);
s2.cancel();
assert.equal(isCancelled, true);
reg.dispose();
});
function fakeSymbolInformation(range: Range, name: string = 'foo'): DocumentSymbol {
return {
name,
detail: 'fake',
kind: SymbolKind.Boolean,
selectionRange: range,
range: range
};
}
function fakeMarker(range: Range): IMarker {
return { ...range, owner: 'ffff', message: 'test', severity: MarkerSeverity.Error, resource: null };
}
test('OutlineElement - updateMarker', function () {
let e0 = new OutlineElement('foo1', null, fakeSymbolInformation(new Range(1, 1, 1, 10)));
let e1 = new OutlineElement('foo2', null, fakeSymbolInformation(new Range(2, 1, 5, 1)));
let e2 = new OutlineElement('foo3', null, fakeSymbolInformation(new Range(6, 1, 10, 10)));
let group = new OutlineGroup('group', null, null, 1);
group.children[e0.id] = e0;
group.children[e1.id] = e1;
group.children[e2.id] = e2;
const data = [fakeMarker(new Range(6, 1, 6, 7)), fakeMarker(new Range(1, 1, 1, 4)), fakeMarker(new Range(10, 2, 14, 1))];
data.sort(Range.compareRangesUsingStarts); // model does this
group.updateMarker(data);
assert.equal(data.length, 0); // all 'stolen'
assert.equal(e0.marker.count, 1);
assert.equal(e1.marker, undefined);
assert.equal(e2.marker.count, 2);
group.updateMarker([]);
assert.equal(e0.marker, undefined);
assert.equal(e1.marker, undefined);
assert.equal(e2.marker, undefined);
});
test('OutlineElement - updateMarker, 2', function () {
let p = new OutlineElement('A', null, fakeSymbolInformation(new Range(1, 1, 11, 1)));
let c1 = new OutlineElement('A/B', null, fakeSymbolInformation(new Range(2, 4, 5, 4)));
let c2 = new OutlineElement('A/C', null, fakeSymbolInformation(new Range(6, 4, 9, 4)));
let group = new OutlineGroup('group', null, null, 1);
group.children[p.id] = p;
p.children[c1.id] = c1;
p.children[c2.id] = c2;
let data = [
fakeMarker(new Range(2, 4, 5, 4))
];
group.updateMarker(data);
assert.equal(p.marker.count, 0);
assert.equal(c1.marker.count, 1);
assert.equal(c2.marker, undefined);
data = [
fakeMarker(new Range(2, 4, 5, 4)),
fakeMarker(new Range(2, 6, 2, 8)),
fakeMarker(new Range(7, 6, 7, 8)),
];
group.updateMarker(data);
assert.equal(p.marker.count, 0);
assert.equal(c1.marker.count, 2);
assert.equal(c2.marker.count, 1);
data = [
fakeMarker(new Range(1, 4, 1, 11)),
fakeMarker(new Range(7, 6, 7, 8)),
];
group.updateMarker(data);
assert.equal(p.marker.count, 1);
assert.equal(c1.marker, undefined);
assert.equal(c2.marker.count, 1);
});
test('OutlineElement - updateMarker/multiple groups', function () {
let model = new class extends OutlineModel {
constructor() {
super(null);
}
readyForTesting() {
this._groups = this.children as any;
}
};
model.children['g1'] = new OutlineGroup('g1', model, null, 1);
model.children['g1'].children['c1'] = new OutlineElement('c1', model.children['g1'], fakeSymbolInformation(new Range(1, 1, 11, 1)));
model.children['g2'] = new OutlineGroup('g2', model, null, 1);
model.children['g2'].children['c2'] = new OutlineElement('c2', model.children['g2'], fakeSymbolInformation(new Range(1, 1, 7, 1)));
model.children['g2'].children['c2'].children['c2.1'] = new OutlineElement('c2.1', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(1, 3, 2, 19)));
model.children['g2'].children['c2'].children['c2.2'] = new OutlineElement('c2.2', model.children['g2'].children['c2'], fakeSymbolInformation(new Range(4, 1, 6, 10)));
model.readyForTesting();
const data = [
fakeMarker(new Range(1, 1, 2, 8)),
fakeMarker(new Range(6, 1, 6, 98)),
];
model.updateMarker(data);
assert.equal(model.children['g1'].children['c1'].marker.count, 2);
assert.equal(model.children['g2'].children['c2'].children['c2.1'].marker.count, 1);
assert.equal(model.children['g2'].children['c2'].children['c2.2'].marker.count, 1);
});
});

View File

@@ -5,14 +5,13 @@
'use strict';
import * as nls from 'vs/nls';
import { HistoryNavigator } from 'vs/base/common/history';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { Disposable } from 'vs/base/common/lifecycle';
import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import * as strings from 'vs/base/common/strings';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorContribution, registerEditorAction, ServicesAccessor, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, ShowPreviousFindTermKeybinding, ShowNextFindTermKeybinding, CONTEXT_FIND_WIDGET_VISIBLE, CONTEXT_FIND_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding, ToggleSearchScopeKeybinding, CONTEXT_FIND_WIDGET_VISIBLE } from 'vs/editor/contrib/find/findModel';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { Delayer } from 'vs/base/common/async';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
@@ -24,8 +23,9 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget';
import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { MenuId } from 'vs/platform/actions/common/actions';
export function getSelectionSearchString(editor: ICodeEditor): string {
let selection = editor.getSelection();
@@ -66,10 +66,9 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
protected _editor: ICodeEditor;
private _findWidgetVisible: IContextKey<boolean>;
protected _state: FindReplaceState;
private _currentHistoryNavigator: HistoryNavigator<string>;
protected _updateHistoryDelayer: Delayer<void>;
private _model: FindModelBoundToEditorModel;
private _storageService: IStorageService;
protected _storageService: IStorageService;
private _clipboardService: IClipboardService;
public static get(editor: ICodeEditor): CommonFindController {
@@ -89,7 +88,6 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
this._clipboardService = clipboardService;
this._updateHistoryDelayer = new Delayer<void>(500);
this._currentHistoryNavigator = new HistoryNavigator<string>();
this._state = this._register(new FindReplaceState());
this.loadQueryState();
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
@@ -139,9 +137,6 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
this.saveQueryState(e);
if (e.updateHistory && e.searchString) {
this._delayedUpdateHistory();
}
if (e.isRevealed) {
if (this._state.isRevealed) {
this._findWidgetVisible.set(true);
@@ -175,24 +170,10 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
}, false);
}
protected _delayedUpdateHistory() {
this._updateHistoryDelayer.trigger(this._updateHistory.bind(this));
}
protected _updateHistory() {
if (this._state.searchString) {
this._currentHistoryNavigator.add(this._state.searchString);
}
}
public getState(): FindReplaceState {
return this._state;
}
public getHistory(): HistoryNavigator<string> {
return this._currentHistoryNavigator;
}
public closeFindWidget(): void {
this._state.change({
isRevealed: false,
@@ -328,22 +309,6 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
return false;
}
public showPreviousFindTerm(): boolean {
let previousTerm = this._currentHistoryNavigator.previous();
if (previousTerm) {
this._state.change({ searchString: previousTerm }, false, false);
}
return true;
}
public showNextFindTerm(): boolean {
let nextTerm = this._currentHistoryNavigator.next();
if (nextTerm) {
this._state.change({ searchString: nextTerm }, false, false);
}
return true;
}
public getGlobalBufferTerm(): string {
if (this._editor.getConfiguration().contribInfo.find.globalFindClipboard
&& this._clipboardService
@@ -422,7 +387,14 @@ export class StartFindAction extends EditorAction {
precondition: null,
kbOpts: {
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_F
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
weight: KeybindingWeight.EditorContrib
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '3_find',
title: nls.localize({ key: 'miFind', comment: ['&& denotes a mnemonic'] }, "&&Find"),
order: 1
}
});
}
@@ -454,7 +426,8 @@ export class StartFindWithSelectionAction extends EditorAction {
primary: null,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_E,
}
},
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -503,7 +476,8 @@ export class NextMatchFindAction extends MatchFindAction {
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] },
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -524,7 +498,8 @@ export class PreviousMatchFindAction extends MatchFindAction {
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Shift | KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] }
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] },
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -569,7 +544,8 @@ export class NextSelectionMatchFindAction extends SelectionMatchFindAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyCode.F3
primary: KeyMod.CtrlCmd | KeyCode.F3,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -589,7 +565,8 @@ export class PreviousSelectionMatchFindAction extends SelectionMatchFindAction {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F3,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -610,7 +587,14 @@ export class StartFindReplaceAction extends EditorAction {
kbOpts: {
kbExpr: null,
primary: KeyMod.CtrlCmd | KeyCode.KEY_H,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F }
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_F },
weight: KeybindingWeight.EditorContrib
},
menubarOpts: {
menuId: MenuId.MenubarEditMenu,
group: '3_find',
title: nls.localize({ key: 'miReplace', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
order: 2
}
});
}
@@ -643,54 +627,6 @@ export class StartFindReplaceAction extends EditorAction {
}
}
export class ShowNextFindTermAction extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.ShowNextFindTermAction,
label: nls.localize('showNextFindTermAction', "Show Next Find Term"),
alias: 'Show Next Find Term',
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus),
primary: ShowNextFindTermKeybinding.primary,
mac: ShowNextFindTermKeybinding.mac,
win: ShowNextFindTermKeybinding.win,
linux: ShowNextFindTermKeybinding.linux
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.showNextFindTerm();
}
}
export class ShowPreviousFindTermAction extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.ShowPreviousFindTermAction,
label: nls.localize('showPreviousFindTermAction', "Show Previous Find Term"),
alias: 'Find Show Previous Find Term',
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
kbExpr: ContextKeyExpr.and(CONTEXT_FIND_INPUT_FOCUSED, EditorContextKeys.focus),
primary: ShowPreviousFindTermKeybinding.primary,
mac: ShowPreviousFindTermKeybinding.mac,
win: ShowPreviousFindTermKeybinding.win,
linux: ShowPreviousFindTermKeybinding.linux
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.showPreviousFindTerm();
}
}
registerEditorContribution(FindController);
registerEditorAction(StartFindAction);
@@ -700,8 +636,6 @@ registerEditorAction(PreviousMatchFindAction);
registerEditorAction(NextSelectionMatchFindAction);
registerEditorAction(PreviousSelectionMatchFindAction);
registerEditorAction(StartFindReplaceAction);
registerEditorAction(ShowNextFindTermAction);
registerEditorAction(ShowPreviousFindTermAction);
const FindCommand = EditorCommand.bindToContribution<CommonFindController>(CommonFindController.get);
@@ -710,7 +644,7 @@ registerEditorCommand(new FindCommand({
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.closeFindWidget(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]
@@ -722,7 +656,7 @@ registerEditorCommand(new FindCommand({
precondition: null,
handler: x => x.toggleCaseSensitive(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: ToggleCaseSensitiveKeybinding.primary,
mac: ToggleCaseSensitiveKeybinding.mac,
@@ -736,7 +670,7 @@ registerEditorCommand(new FindCommand({
precondition: null,
handler: x => x.toggleWholeWords(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: ToggleWholeWordKeybinding.primary,
mac: ToggleWholeWordKeybinding.mac,
@@ -750,7 +684,7 @@ registerEditorCommand(new FindCommand({
precondition: null,
handler: x => x.toggleRegex(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: ToggleRegexKeybinding.primary,
mac: ToggleRegexKeybinding.mac,
@@ -764,7 +698,7 @@ registerEditorCommand(new FindCommand({
precondition: null,
handler: x => x.toggleSearchScope(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: ToggleSearchScopeKeybinding.primary,
mac: ToggleSearchScopeKeybinding.mac,
@@ -778,7 +712,7 @@ registerEditorCommand(new FindCommand({
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replace(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_1
}
@@ -789,7 +723,7 @@ registerEditorCommand(new FindCommand({
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replaceAll(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Enter
}
@@ -800,7 +734,7 @@ registerEditorCommand(new FindCommand({
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.selectAllMatches(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(5),
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Alt | KeyCode.Enter
}

View File

@@ -46,12 +46,6 @@ export const ToggleSearchScopeKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.KEY_L,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_L }
};
export const ShowPreviousFindTermKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.UpArrow
};
export const ShowNextFindTermKeybinding: IKeybindings = {
primary: KeyMod.Alt | KeyCode.DownArrow
};
export const FIND_IDS = {
StartFindAction: 'actions.find',
@@ -68,9 +62,7 @@ export const FIND_IDS = {
ToggleSearchScopeCommand: 'toggleFindInSelection',
ReplaceOneAction: 'editor.action.replaceOne',
ReplaceAllAction: 'editor.action.replaceAll',
SelectAllMatchesAction: 'editor.action.selectAllMatches',
ShowPreviousFindTermAction: 'find.history.showPrevious',
ShowNextFindTermAction: 'find.history.showNext'
SelectAllMatchesAction: 'editor.action.selectAllMatches'
};
export const MATCHES_LIMIT = 19999;

View File

@@ -53,38 +53,38 @@ export class FindOptionsWidget extends Widget implements IOverlayWidget {
this.caseSensitive = this._register(new CaseSensitiveCheckbox({
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleCaseSensitiveCommand),
isChecked: this._state.matchCase,
onChange: (viaKeyboard) => {
this._state.change({
matchCase: this.caseSensitive.checked
}, false);
},
inputActiveOptionBorder: inputActiveOptionBorderColor
}));
this._domNode.appendChild(this.caseSensitive.domNode);
this._register(this.caseSensitive.onChange(() => {
this._state.change({
matchCase: this.caseSensitive.checked
}, false);
}));
this.wholeWords = this._register(new WholeWordsCheckbox({
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleWholeWordCommand),
isChecked: this._state.wholeWord,
onChange: (viaKeyboard) => {
this._state.change({
wholeWord: this.wholeWords.checked
}, false);
},
inputActiveOptionBorder: inputActiveOptionBorderColor
}));
this._domNode.appendChild(this.wholeWords.domNode);
this._register(this.wholeWords.onChange(() => {
this._state.change({
wholeWord: this.wholeWords.checked
}, false);
}));
this.regex = this._register(new RegexCheckbox({
appendTitle: this._keybindingLabelFor(FIND_IDS.ToggleRegexCommand),
isChecked: this._state.isRegex,
onChange: (viaKeyboard) => {
this._state.change({
isRegex: this.regex.checked
}, false);
},
inputActiveOptionBorder: inputActiveOptionBorderColor
}));
this._domNode.appendChild(this.regex.domNode);
this._register(this.regex.onChange(() => {
this._state.change({
isRegex: this.regex.checked
}, false);
}));
this._editor.addOverlayWidget(this);

View File

@@ -11,12 +11,13 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import * as strings from 'vs/base/common/strings';
import { Delayer } from 'vs/base/common/async';
import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { IMessage as InputBoxMessage, InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IMessage as InputBoxMessage, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { Widget } from 'vs/base/browser/ui/widget';
import { Sash, IHorizontalSashLayoutProvider, ISashEvent, Orientation } from 'vs/base/browser/ui/sash/sash';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -28,7 +29,8 @@ import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/c
import { ITheme, registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService';
import { Color } from 'vs/base/common/color';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, errorForeground, editorWidgetBorder, editorFindMatchBorder, editorFindMatchHighlightBorder, editorFindRangeHighlightBorder } from 'vs/platform/theme/common/colorRegistry';
import { editorFindRangeHighlight, editorFindMatch, editorFindMatchHighlight, contrastBorder, inputBackground, editorWidgetBackground, inputActiveOptionBorder, widgetShadow, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, errorForeground, editorWidgetBorder, editorFindMatchBorder, editorFindMatchHighlightBorder, editorFindRangeHighlightBorder, editorWidgetResizeBorder } from 'vs/platform/theme/common/colorRegistry';
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
export interface IFindController {
@@ -87,10 +89,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _controller: IFindController;
private readonly _contextViewProvider: IContextViewProvider;
private readonly _keybindingService: IKeybindingService;
private readonly _contextKeyService: IContextKeyService;
private _domNode: HTMLElement;
private _findInput: FindInput;
private _replaceInputBox: InputBox;
private _replaceInputBox: HistoryInputBox;
private _toggleReplaceBtn: SimpleButton;
private _matchesCount: HTMLElement;
@@ -113,6 +116,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _resizeSash: Sash;
private _resized: boolean;
private _updateHistoryDelayer: Delayer<void>;
constructor(
codeEditor: ICodeEditor,
@@ -129,10 +133,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._state = state;
this._contextViewProvider = contextViewProvider;
this._keybindingService = keybindingService;
this._contextKeyService = contextKeyService;
this._isVisible = false;
this._isReplaceVisible = false;
this._updateHistoryDelayer = new Delayer<void>(500);
this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e)));
this._buildDomNode();
this._updateButtons();
@@ -155,7 +161,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._updateToggleSelectionFindButton();
}
}));
this._register(this._codeEditor.onDidFocusEditor(() => {
this._register(this._codeEditor.onDidFocusEditorWidget(() => {
if (this._isVisible) {
let globalBufferTerm = this._controller.getGlobalBufferTerm();
if (globalBufferTerm && globalBufferTerm !== this._state.searchString) {
@@ -295,6 +301,22 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (e.searchString || e.currentMatch) {
this._layoutViewZone();
}
if (e.updateHistory) {
this._delayedUpdateHistory();
}
}
private _delayedUpdateHistory() {
this._updateHistoryDelayer.trigger(this._updateHistory.bind(this));
}
private _updateHistory() {
if (this._state.searchString) {
this._findInput.inputBox.addToHistory();
}
if (this._state.replaceString) {
this._replaceInputBox.addToHistory();
}
}
private _updateMatchesCount(): void {
@@ -675,7 +697,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _buildFindPart(): HTMLElement {
// Find input
this._findInput = this._register(new FindInput(null, this._contextViewProvider, {
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, {
width: FIND_INPUT_AREA_WIDTH,
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
@@ -698,12 +720,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return { content: e.message };
}
}
}));
}, this._contextKeyService));
this._findInput.setRegex(!!this._state.isRegex);
this._findInput.setCaseSensitive(!!this._state.matchCase);
this._findInput.setWholeWords(!!this._state.wholeWord);
this._register(this._findInput.onKeyDown((e) => this._onFindInputKeyDown(e)));
this._register(this._findInput.onInput(() => {
this._register(this._findInput.inputBox.onDidChange(() => {
this._state.change({ searchString: this._findInput.getValue() }, true);
}));
this._register(this._findInput.onDidOptionChange(() => {
@@ -804,10 +826,11 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
let replaceInput = document.createElement('div');
replaceInput.className = 'replace-input';
replaceInput.style.width = REPLACE_INPUT_AREA_WIDTH + 'px';
this._replaceInputBox = this._register(new InputBox(replaceInput, null, {
this._replaceInputBox = this._register(new ContextScopedHistoryInputBox(replaceInput, null, {
ariaLabel: NLS_REPLACE_INPUT_LABEL,
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER
}));
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER,
history: []
}, this._contextKeyService));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'input', (e) => {
@@ -1109,8 +1132,14 @@ registerThemingParticipant((theme, collector) => {
collector.addRule(`.monaco-editor .find-widget.no-results .matchesCount { color: ${error}; }`);
}
const border = theme.getColor(editorWidgetBorder);
if (border) {
collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`);
const resizeBorderBackground = theme.getColor(editorWidgetResizeBorder);
if (resizeBorderBackground) {
collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${resizeBorderBackground}; width: 3px !important; margin-left: -4px;}`);
} else {
const border = theme.getColor(editorWidgetBorder);
if (border) {
collector.addRule(`.monaco-editor .find-widget .monaco-sash { background-color: ${border}; width: 3px !important; margin-left: -4px;}`);
}
}
});

View File

@@ -30,7 +30,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand {
public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void {
if (this._ranges.length > 0) {
// Collect all edit operations
var ops: IEditOperation[] = [];
let ops: IEditOperation[] = [];
for (let i = 0; i < this._ranges.length; i++) {
ops.push({
range: this._ranges[i],
@@ -44,8 +44,8 @@ export class ReplaceAllCommand implements editorCommon.ICommand {
});
// Merge operations that touch each other
var resultOps: IEditOperation[] = [];
var previousOp = ops[0];
let resultOps: IEditOperation[] = [];
let previousOp = ops[0];
for (let i = 1; i < ops.length; i++) {
if (previousOp.range.endLineNumber === ops[i].range.startLineNumber && previousOp.range.endColumn === ops[i].range.startColumn) {
// These operations are one after another and can be merged
@@ -58,7 +58,7 @@ export class ReplaceAllCommand implements editorCommon.ICommand {
}
resultOps.push(previousOp);
for (var i = 0; i < resultOps.length; i++) {
for (let i = 0; i < resultOps.length; i++) {
builder.addEditOperation(resultOps[i].range, resultOps[i].text);
}
}

View File

@@ -7,7 +7,6 @@ import 'vs/css!./simpleFindWidget';
import * as nls from 'vs/nls';
import { Widget } from 'vs/base/browser/ui/widget';
import { Delayer } from 'vs/base/common/async';
import { HistoryNavigator } from 'vs/base/common/history';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import * as dom from 'vs/base/browser/dom';
import { FindInput } from 'vs/base/browser/ui/findinput/findInput';
@@ -15,6 +14,8 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { registerThemingParticipant, ITheme } from 'vs/platform/theme/common/themeService';
import { inputBackground, inputActiveOptionBorder, inputForeground, inputBorder, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationErrorBackground, inputValidationErrorBorder, editorWidgetBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { SimpleButton } from './findWidget';
import { ContextScopedFindInput } from 'vs/platform/widget/browser/contextScopedHistoryWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
const NLS_FIND_INPUT_LABEL = nls.localize('label.find', "Find");
const NLS_FIND_INPUT_PLACEHOLDER = nls.localize('placeholder.find', "Find");
@@ -29,21 +30,20 @@ export abstract class SimpleFindWidget extends Widget {
private _isVisible: boolean;
private _focusTracker: dom.IFocusTracker;
private _findInputFocusTracker: dom.IFocusTracker;
private _findHistory: HistoryNavigator<string>;
private _updateHistoryDelayer: Delayer<void>;
constructor(
@IContextViewService private readonly _contextViewService: IContextViewService
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService
) {
super();
this._findInput = this._register(new FindInput(null, this._contextViewService, {
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewService, {
label: NLS_FIND_INPUT_LABEL,
placeholder: NLS_FIND_INPUT_PLACEHOLDER,
}));
}, contextKeyService));
// Find History with update delayer
this._findHistory = new HistoryNavigator<string>();
this._updateHistoryDelayer = new Delayer<void>(500);
this.oninput(this._findInput.domNode, (e) => {
@@ -133,6 +133,10 @@ export abstract class SimpleFindWidget extends Widget {
return this._findInput.getValue();
}
public get focusTracker(): dom.IFocusTracker {
return this._findInputFocusTracker;
}
public updateTheme(theme: ITheme): void {
const inputStyles = {
inputActiveOptionBorder: theme.getColor(inputActiveOptionBorder),
@@ -149,11 +153,12 @@ export abstract class SimpleFindWidget extends Widget {
this._findInput.style(inputStyles);
}
dipose() {
dispose() {
super.dispose();
if (this._domNode && this._domNode.parentElement) {
this._domNode.parentElement.removeChild(this._domNode);
this._domNode = undefined;
}
}
@@ -196,23 +201,7 @@ export abstract class SimpleFindWidget extends Widget {
}
protected _updateHistory() {
if (this.inputValue) {
this._findHistory.add(this._findInput.getValue());
}
}
public showNextFindTerm() {
const next = this._findHistory.next();
if (next) {
this._findInput.setValue(next);
}
}
public showPreviousFindTerm() {
const previous = this._findHistory.previous();
if (previous) {
this._findInput.setValue(previous);
}
this._findInput.inputBox.addToHistory();
}
}

View File

@@ -6,7 +6,6 @@
import * as assert from 'assert';
import { TPromise } from 'vs/base/common/winjs.base';
import { Emitter } from 'vs/base/common/event';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
@@ -14,7 +13,6 @@ import { Range } from 'vs/editor/common/core/range';
import * as platform from 'vs/base/common/platform';
import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, StartFindAction, NextSelectionMatchFindAction } from 'vs/editor/contrib/find/findController';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { HistoryNavigator } from 'vs/base/common/history';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -28,8 +26,6 @@ export class TestFindController extends CommonFindController {
public delayUpdateHistory: boolean = false;
public delayedUpdateHistoryPromise: TPromise<void>;
private readonly _delayedUpdateHistoryEvent: Emitter<void> = new Emitter<void>();
constructor(
editor: ICodeEditor,
@IContextKeyService contextKeyService: IContextKeyService,
@@ -47,28 +43,6 @@ export class TestFindController extends CommonFindController {
this.hasFocus = true;
}
}
protected _delayedUpdateHistory() {
if (!this.delayedUpdateHistoryPromise) {
this.delayedUpdateHistoryPromise = new TPromise<void>((c, e) => {
const disposable = this._delayedUpdateHistoryEvent.event(() => {
disposable.dispose();
this.delayedUpdateHistoryPromise = null;
c(null);
});
});
}
if (this.delayUpdateHistory) {
super._delayedUpdateHistory();
} else {
this._updateHistory();
}
}
protected _updateHistory() {
super._updateHistory();
this._delayedUpdateHistoryEvent.fire();
}
}
function fromRange(rng: Range): number[] {
@@ -87,7 +61,7 @@ suite('FindController', () => {
if (platform.isMacintosh) {
serviceCollection.set(IClipboardService, <any>{
readFindText: _ => clipboardState,
readFindText: () => clipboardState,
writeFindText: (value: any) => { clipboardState = value; }
});
}
@@ -303,116 +277,6 @@ suite('FindController', () => {
});
});
test('find term is added to history on state change', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory()));
});
});
test('find term is added with delay', (done) => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.delayUpdateHistory = true;
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.delayedUpdateHistoryPromise.then(() => {
assert.deepEqual(['3'], toArray(findController.getHistory()));
done();
}, error => done(error));
});
});
test('show previous find term', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.showPreviousFindTerm();
assert.deepEqual('2', findController.getState().searchString);
});
});
test('show previous find term do not update history', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.showPreviousFindTerm();
assert.deepEqual(['1', '2', '3'], toArray(findController.getHistory()));
});
});
test('show next find term', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.getState().change({ searchString: '4' }, false);
findController.showPreviousFindTerm();
findController.showPreviousFindTerm();
findController.showNextFindTerm();
assert.deepEqual('3', findController.getState().searchString);
});
});
test('show next find term do not update history', () => {
withTestCodeEditor([
'var x = (3 * 5)',
'var y = (3 * 5)',
'var z = (3 * 5)',
], { serviceCollection: serviceCollection }, (editor, cursor) => {
clipboardState = '';
let findController = editor.registerAndInstantiateContribution<TestFindController>(TestFindController);
findController.getState().change({ searchString: '1' }, false);
findController.getState().change({ searchString: '2' }, false);
findController.getState().change({ searchString: '3' }, false);
findController.getState().change({ searchString: '4' }, false);
findController.showPreviousFindTerm();
findController.showPreviousFindTerm();
findController.showNextFindTerm();
assert.deepEqual(['1', '2', '3', '4'], toArray(findController.getHistory()));
});
});
test('issue #18111: Regex replace with single space replaces with no space', () => {
withTestCodeEditor([
'HRESULT OnAmbientPropertyChange(DISPID dispid);'
@@ -465,17 +329,6 @@ suite('FindController', () => {
});
});
function toArray(historyNavigator: HistoryNavigator<string>): string[] {
let result = [];
historyNavigator.first();
if (historyNavigator.current()) {
do {
result.push(historyNavigator.current());
} while (historyNavigator.next());
}
return result;
}
test('issue #38232: Find Next Selection, regex enabled', () => {
withTestCodeEditor([
'([funny]',

View File

@@ -9,7 +9,7 @@ import 'vs/css!./folding';
import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
import { escapeRegExpCharacters } from 'vs/base/common/strings';
import { RunOnceScheduler, Delayer, asWinJsPromise } from 'vs/base/common/async';
import { RunOnceScheduler, Delayer, CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -29,13 +29,23 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
import { IndentRangeProvider } from 'vs/editor/contrib/folding/indentRangeProvider';
import { IPosition } from 'vs/editor/common/core/position';
import { FoldingRangeProviderRegistry, FoldingRangeKind } from 'vs/editor/common/modes';
import { SyntaxRangeProvider } from './syntaxRangeProvider';
import { SyntaxRangeProvider, ID_SYNTAX_PROVIDER } from './syntaxRangeProvider';
import { CancellationToken } from 'vs/base/common/cancellation';
import { InitializingRangeProvider, ID_INIT_PROVIDER } from 'vs/editor/contrib/folding/intializingRangeProvider';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export const ID = 'editor.contrib.folding';
export interface RangeProvider {
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions>;
readonly id: string;
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions>;
dispose(): void;
}
interface FoldingStateMemento {
collapsedRegions?: CollapseMemento;
lineCount?: number;
provider?: string;
}
export class FoldingController implements IEditorContribution {
@@ -58,7 +68,9 @@ export class FoldingController implements IEditorContribution {
private hiddenRangeModel: HiddenRangeModel;
private rangeProvider: RangeProvider;
private foldingRegionPromise: TPromise<FoldingRegions>;
private foldingRegionPromise: CancelablePromise<FoldingRegions>;
private foldingStateMemento: FoldingStateMemento;
private foldingModelPromise: TPromise<FoldingModel>;
private updateScheduler: Delayer<FoldingModel>;
@@ -119,14 +131,15 @@ export class FoldingController implements IEditorContribution {
/**
* Store view state.
*/
public saveViewState(): { collapsedRegions?: CollapseMemento, lineCount?: number } {
public saveViewState(): FoldingStateMemento {
let model = this.editor.getModel();
if (!model || !this._isEnabled || model.isTooLargeForTokenization()) {
return {};
}
if (this.foldingModel) { // disposed ?
let collapsedRegions = this.foldingModel.isInitialized ? this.foldingModel.getMemento() : this.hiddenRangeModel.getMemento();
return { collapsedRegions, lineCount: model.getLineCount() };
let provider = this.rangeProvider ? this.rangeProvider.id : void 0;
return { collapsedRegions, lineCount: model.getLineCount(), provider };
}
return void 0;
}
@@ -134,7 +147,7 @@ export class FoldingController implements IEditorContribution {
/**
* Restore view state.
*/
public restoreViewState(state: { collapsedRegions?: CollapseMemento, lineCount?: number }): void {
public restoreViewState(state: FoldingStateMemento): void {
let model = this.editor.getModel();
if (!model || !this._isEnabled || model.isTooLargeForTokenization()) {
return;
@@ -143,6 +156,10 @@ export class FoldingController implements IEditorContribution {
return;
}
if (state.provider === ID_SYNTAX_PROVIDER || state.provider === ID_INIT_PROVIDER) {
this.foldingStateMemento = state;
}
// set the hidden ranges right away, before waiting for the folding model.
if (this.hiddenRangeModel.applyMemento(state.collapsedRegions)) {
this.getFoldingModel().then(foldingModel => {
@@ -190,27 +207,44 @@ export class FoldingController implements IEditorContribution {
this.foldingModelPromise = null;
this.hiddenRangeModel = null;
this.cursorChangedScheduler = null;
this.foldingStateMemento = null;
if (this.rangeProvider) {
this.rangeProvider.dispose();
}
this.rangeProvider = null;
}
});
this.onModelContentChanged();
}
private onFoldingStrategyChanged() {
if (this.rangeProvider) {
this.rangeProvider.dispose();
}
this.rangeProvider = null;
this.onModelContentChanged();
}
private getRangeProvider(): RangeProvider {
if (!this.rangeProvider) {
if (this._useFoldingProviders) {
let foldingProviders = FoldingRangeProviderRegistry.ordered(this.foldingModel.textModel);
this.rangeProvider = foldingProviders.length ? new SyntaxRangeProvider(foldingProviders) : new IndentRangeProvider();
} else {
this.rangeProvider = new IndentRangeProvider();
private getRangeProvider(editorModel: ITextModel): RangeProvider {
if (this.rangeProvider) {
return this.rangeProvider;
}
this.rangeProvider = new IndentRangeProvider(editorModel); // fallback
if (this._useFoldingProviders) {
let foldingProviders = FoldingRangeProviderRegistry.ordered(this.foldingModel.textModel);
if (foldingProviders.length === 0 && this.foldingStateMemento) {
this.rangeProvider = new InitializingRangeProvider(editorModel, this.foldingStateMemento.collapsedRegions, () => {
// if after 30 the InitializingRangeProvider is still not replaced, force a refresh
this.foldingStateMemento = null;
this.onFoldingStrategyChanged();
}, 30000);
return this.rangeProvider; // keep memento in case there are still no foldingProviders on the next request.
} else if (foldingProviders.length > 0) {
this.rangeProvider = new SyntaxRangeProvider(editorModel, foldingProviders);
}
}
this.foldingStateMemento = null;
return this.rangeProvider;
}
@@ -228,8 +262,8 @@ export class FoldingController implements IEditorContribution {
if (!this.foldingModel) { // null if editor has been disposed, or folding turned off
return null;
}
let foldingRegionPromise = this.foldingRegionPromise = asWinJsPromise<FoldingRegions>(token => this.getRangeProvider().compute(this.foldingModel.textModel, token));
return foldingRegionPromise.then(foldingRanges => {
let foldingRegionPromise = this.foldingRegionPromise = createCancelablePromise(token => this.getRangeProvider(this.foldingModel.textModel).compute(token));
return TPromise.wrap(foldingRegionPromise.then(foldingRanges => {
if (foldingRanges && foldingRegionPromise === this.foldingRegionPromise) { // new request or cancelled in the meantime?
// some cursors might have moved into hidden regions, make sure they are in expanded regions
let selections = this.editor.getSelections();
@@ -237,7 +271,7 @@ export class FoldingController implements IEditorContribution {
this.foldingModel.update(foldingRanges, selectionLineNumbers);
}
return this.foldingModel;
});
}));
});
}
}
@@ -298,7 +332,7 @@ export class FoldingController implements IEditorContribution {
switch (e.target.type) {
case MouseTargetType.GUTTER_LINE_DECORATIONS:
const data = e.target.detail as IMarginData;
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth;
const gutterOffsetX = data.offsetX - data.glyphMarginWidth - data.lineNumbersWidth - data.glyphMarginLeft;
// TODO@joao TODO@alex TODO@martin this is such that we don't collide with dirty diff
if (gutterOffsetX <= 10) {
@@ -453,7 +487,8 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_CLOSE_SQUARE_BRACKET,
mac: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_CLOSE_SQUARE_BRACKET
}
},
weight: KeybindingWeight.EditorContrib
},
description: {
description: 'Unfold the content in the editor',
@@ -462,7 +497,7 @@ class UnfoldAction extends FoldingAction<FoldingArguments> {
name: 'Unfold editor argument',
description: `Property-value pairs that can be passed through this argument:
* 'levels': Number of levels to unfold. If not set, defaults to 1.
* 'direction': If 'up', unfold given number of levels up otherwise unfolds down
* 'direction': If 'up', unfold given number of levels up otherwise unfolds down.
* 'selectionLines': The start lines (0-based) of the editor selections to apply the unfold action to. If not set, the active selection(s) will be used.
`,
constraint: foldingArgumentsConstraint
@@ -493,7 +528,8 @@ class UnFoldRecursivelyAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_CLOSE_SQUARE_BRACKET),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -516,7 +552,8 @@ class FoldAction extends FoldingAction<FoldingArguments> {
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.US_OPEN_SQUARE_BRACKET,
mac: {
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_OPEN_SQUARE_BRACKET
}
},
weight: KeybindingWeight.EditorContrib
},
description: {
description: 'Fold the content in the editor',
@@ -524,8 +561,8 @@ class FoldAction extends FoldingAction<FoldingArguments> {
{
name: 'Fold editor argument',
description: `Property-value pairs that can be passed through this argument:
* 'levels': Number of levels to fold. Defaults to 1
* 'direction': If 'up', folds given number of levels up otherwise folds down
* 'levels': Number of levels to fold. Defaults to 1.
* 'direction': If 'up', folds given number of levels up otherwise folds down.
* 'selectionLines': The start lines (0-based) of the editor selections to apply the fold action to. If not set, the active selection(s) will be used.
`,
constraint: foldingArgumentsConstraint
@@ -556,7 +593,8 @@ class FoldRecursivelyAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_OPEN_SQUARE_BRACKET),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -577,7 +615,8 @@ class FoldAllBlockCommentsAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.US_SLASH),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -605,7 +644,8 @@ class FoldAllRegionsAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_8),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -633,7 +673,8 @@ class UnfoldAllRegionsAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_9),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -661,7 +702,8 @@ class FoldAllAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_0),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -681,7 +723,8 @@ class UnfoldAllAction extends FoldingAction<void> {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_J),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -724,7 +767,8 @@ for (let i = 1; i <= 7; i++) {
precondition: null,
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i))
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | (KeyCode.KEY_0 + i)),
weight: KeybindingWeight.EditorContrib
}
})
);

View File

@@ -16,12 +16,24 @@ import { CancellationToken } from 'vs/base/common/cancellation';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
export const ID_INDENT_PROVIDER = 'indent';
export class IndentRangeProvider implements RangeProvider {
compute(editorModel: ITextModel, cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(editorModel.getLanguageIdentifier().id);
readonly id = ID_INDENT_PROVIDER;
readonly decorations;
constructor(private editorModel: ITextModel) {
}
dispose() {
}
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRules = LanguageConfigurationRegistry.getFoldingRules(this.editorModel.getLanguageIdentifier().id);
let offSide = foldingRules && foldingRules.offSide;
let markers = foldingRules && foldingRules.markers;
return TPromise.as(computeRanges(editorModel, offSide, markers));
return TPromise.as(computeRanges(this.editorModel, offSide, markers));
}
}
@@ -79,13 +91,13 @@ export class RangesCollector {
}
const tabSize = model.getOptions().tabSize;
// reverse and create arrays of the exact length
let startIndexes = new Uint32Array(entries);
let endIndexes = new Uint32Array(entries);
let startIndexes = new Uint32Array(this._foldingRangesLimit);
let endIndexes = new Uint32Array(this._foldingRangesLimit);
for (let i = this._length - 1, k = 0; i >= 0; i--) {
let startIndex = this._startIndexes[i];
let lineContent = model.getLineContent(startIndex);
let indent = TextModel.computeIndentLevel(lineContent, tabSize);
if (indent < maxIndent) {
if (indent < maxIndent || (indent === maxIndent && entries++ < this._foldingRangesLimit)) {
startIndexes[k] = startIndex;
endIndexes[k] = this._endIndexes[i];
k++;

View File

@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ITextModel, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model';
import { FoldingRegions, ILineRange } from 'vs/editor/contrib/folding/foldingRanges';
import { RangeProvider } from './folding';
import { TPromise } from 'vs/base/common/winjs.base';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IFoldingRangeData, sanitizeRanges } from 'vs/editor/contrib/folding/syntaxRangeProvider';
export const ID_INIT_PROVIDER = 'init';
export class InitializingRangeProvider implements RangeProvider {
readonly id = ID_INIT_PROVIDER;
private decorationIds: string[] | undefined;
private timeout: number;
constructor(private editorModel: ITextModel, initialRanges: ILineRange[], onTimeout: () => void, timeoutTime: number) {
if (initialRanges.length) {
let toDecorationRange = (range: ILineRange): IModelDeltaDecoration => {
return {
range: {
startLineNumber: range.startLineNumber,
startColumn: 0,
endLineNumber: range.endLineNumber,
endColumn: editorModel.getLineLength(range.endLineNumber)
},
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
}
};
};
this.decorationIds = editorModel.deltaDecorations([], initialRanges.map(toDecorationRange));
this.timeout = setTimeout(onTimeout, timeoutTime);
}
}
dispose(): void {
if (this.decorationIds) {
this.editorModel.deltaDecorations(this.decorationIds, []);
this.decorationIds = void 0;
}
if (typeof this.timeout === 'number') {
clearTimeout(this.timeout);
this.timeout = void 0;
}
}
compute(cancelationToken: CancellationToken): Thenable<FoldingRegions> {
let foldingRangeData: IFoldingRangeData[] = [];
if (this.decorationIds) {
for (let id of this.decorationIds) {
let range = this.editorModel.getDecorationRange(id);
if (range) {
foldingRangeData.push({ start: range.startLineNumber, end: range.endLineNumber, rank: 1 });
}
}
}
return TPromise.as(sanitizeRanges(foldingRangeData, Number.MAX_VALUE));
}
}

View File

@@ -14,42 +14,46 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { MAX_LINE_NUMBER, FoldingRegions } from './foldingRanges';
import { CancellationToken } from 'vs/base/common/cancellation';
const MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT = 5000;
const MAX_FOLDING_REGIONS = 5000;
export interface IFoldingRangeData extends FoldingRange {
rank: number;
}
const foldingContext: FoldingContext = {
maxRanges: MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT
};
export const ID_SYNTAX_PROVIDER = 'syntax';
export class SyntaxRangeProvider implements RangeProvider {
constructor(private providers: FoldingRangeProvider[]) {
readonly id = ID_SYNTAX_PROVIDER;
constructor(private editorModel: ITextModel, private providers: FoldingRangeProvider[], private limit = MAX_FOLDING_REGIONS) {
}
compute(model: ITextModel, cancellationToken: CancellationToken): Thenable<FoldingRegions> {
return collectSyntaxRanges(this.providers, model, cancellationToken).then(ranges => {
compute(cancellationToken: CancellationToken): Thenable<FoldingRegions> {
return collectSyntaxRanges(this.providers, this.editorModel, cancellationToken).then(ranges => {
if (ranges) {
let res = sanitizeRanges(ranges);
let res = sanitizeRanges(ranges, this.limit);
return res;
}
return null;
});
}
dispose() {
}
}
function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextModel, cancellationToken: CancellationToken): Thenable<IFoldingRangeData[] | null> {
let promises = providers.map(provider => toThenable(provider.provideFoldingRanges(model, foldingContext, cancellationToken)));
return TPromise.join(promises).then(results => {
let rangeData: IFoldingRangeData[] = null;
if (cancellationToken.isCancellationRequested) {
return null;
}
for (let i = 0; i < results.length; i++) {
let ranges = results[i];
let rangeData: IFoldingRangeData[] = null;
let promises = providers.map((provider, i) => {
return toThenable(provider.provideFoldingRanges(model, foldingContext, cancellationToken)).then(ranges => {
if (cancellationToken.isCancellationRequested) {
return;
}
if (Array.isArray(ranges)) {
if (!Array.isArray(rangeData)) {
rangeData = [];
@@ -61,10 +65,11 @@ function collectSyntaxRanges(providers: FoldingRangeProvider[], model: ITextMode
}
}
}
}
}, onUnexpectedExternalError);
});
return TPromise.join(promises).then(_ => {
return rangeData;
}, onUnexpectedExternalError);
});
}
export class RangesCollector {
@@ -123,12 +128,13 @@ export class RangesCollector {
entries += n;
}
}
let startIndexes = new Uint32Array(entries);
let endIndexes = new Uint32Array(entries);
let startIndexes = new Uint32Array(this._foldingRangesLimit);
let endIndexes = new Uint32Array(this._foldingRangesLimit);
let types = [];
for (let i = 0, k = 0; i < this._length; i++) {
let level = this._nestingLevels[i];
if (level < maxLevel) {
if (level < maxLevel || (level === maxLevel && entries++ < this._foldingRangesLimit)) {
startIndexes[k] = this._startIndexes[i];
endIndexes[k] = this._endIndexes[i];
types[k] = this._types[i];
@@ -142,7 +148,7 @@ export class RangesCollector {
}
export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions {
export function sanitizeRanges(rangeData: IFoldingRangeData[], limit: number): FoldingRegions {
let sorted = rangeData.sort((d1, d2) => {
let diff = d1.start - d2.start;
@@ -151,7 +157,7 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions {
}
return diff;
});
let collector = new RangesCollector(MAX_FOLDING_REGIONS_FOR_INDENT_LIMIT);
let collector = new RangesCollector(limit);
let top: IFoldingRangeData = null;
let previous = [];
@@ -165,12 +171,16 @@ export function sanitizeRanges(rangeData: IFoldingRangeData[]): FoldingRegions {
previous.push(top);
top = entry;
collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
} else if (entry.start > top.end) {
do {
top = previous.pop();
} while (top && entry.start > top.end);
previous.push(top);
top = entry;
} else {
if (entry.start > top.end) {
do {
top = previous.pop();
} while (top && entry.start > top.end);
if (top) {
previous.push(top);
}
top = entry;
}
collector.add(entry.start, entry.end, entry.kind && entry.kind.value, previous.length);
}
}

View File

@@ -9,16 +9,16 @@ import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';
import { TextModel } from 'vs/editor/common/model/textModel';
interface IndentRange {
startLineNumber: number;
endLineNumber: number;
start: number;
end: number;
}
suite('Indentation Folding', () => {
function r(startLineNumber: number, endLineNumber: number): IndentRange {
return { startLineNumber, endLineNumber };
function r(start: number, end: number): IndentRange {
return { start, end };
}
test('Limit By indent', () => {
test('Limit by indent', () => {
let lines = [
@@ -39,39 +39,39 @@ suite('Indentation Folding', () => {
/* 15*/ 'A',
/* 16*/ ' A'
];
let r1 = r(1, 14);
let r2 = r(3, 11);
let r3 = r(4, 5);
let r4 = r(6, 11);
let r5 = r(8, 9);
let r6 = r(10, 11);
let r7 = r(12, 14);
let r8 = r(13, 14);
let r9 = r(15, 16);
let r1 = r(1, 14); //0
let r2 = r(3, 11); //1
let r3 = r(4, 5); //2
let r4 = r(6, 11); //2
let r5 = r(8, 9); //3
let r6 = r(10, 11); //3
let r7 = r(12, 14); //1
let r8 = r(13, 14);//4
let r9 = r(15, 16);//0
let model = TextModel.createFromString(lines.join('\n'));
function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = computeRanges(model, true, null, maxEntries);
assert.ok(indentRanges.length <= maxEntries, 'max ' + message);
assert.equal(indentRanges.length, expectedRanges.length, 'len ' + message);
for (let i = 0; i < expectedRanges.length; i++) {
assert.equal(indentRanges.getStartLineNumber(i), expectedRanges[i].startLineNumber, 'start ' + message);
assert.equal(indentRanges.getEndLineNumber(i), expectedRanges[i].endLineNumber, 'end ' + message);
let actual = [];
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
}
assert.deepEqual(actual, expectedRanges, message);
}
assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1');
assertLimit(9, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '2');
assertLimit(8, [r1, r2, r3, r4, r5, r6, r7, r9], '3');
assertLimit(7, [r1, r2, r3, r4, r7, r9], '4');
assertLimit(6, [r1, r2, r3, r4, r7, r9], '5');
assertLimit(5, [r1, r2, r7, r9], '6');
assertLimit(4, [r1, r2, r7, r9], '7');
assertLimit(3, [r1, r9], '8');
assertLimit(2, [r1, r9], '9');
assertLimit(1, [], '10');
assertLimit(0, [], '11');
assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
assertLimit(9, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '9');
assertLimit(8, [r1, r2, r3, r4, r5, r6, r7, r9], '8');
assertLimit(7, [r1, r2, r3, r4, r5, r7, r9], '7');
assertLimit(6, [r1, r2, r3, r4, r7, r9], '6');
assertLimit(5, [r1, r2, r3, r7, r9], '5');
assertLimit(4, [r1, r2, r7, r9], '4');
assertLimit(3, [r1, r2, r9], '3');
assertLimit(2, [r1, r9], '2');
assertLimit(1, [r1], '1');
assertLimit(0, [], '0');
});
});

View File

@@ -0,0 +1,100 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { TextModel } from 'vs/editor/common/model/textModel';
import { SyntaxRangeProvider } from 'vs/editor/contrib/folding/syntaxRangeProvider';
import { FoldingRangeProvider, FoldingRange, FoldingContext } from 'vs/editor/common/modes';
import { ITextModel } from 'vs/editor/common/model';
import { CancellationToken } from 'vs/base/common/cancellation';
interface IndentRange {
start: number;
end: number;
}
class TestFoldingRangeProvider implements FoldingRangeProvider {
constructor(private model: ITextModel, private ranges: IndentRange[]) {
}
provideFoldingRanges(model: ITextModel, context: FoldingContext, token: CancellationToken): FoldingRange[] {
if (model === this.model) {
return this.ranges;
}
return null;
}
}
suite('Syntax folding', () => {
function r(start: number, end: number): IndentRange {
return { start, end };
}
test('Limit by nesting level', async () => {
let lines = [
/* 1*/ '{',
/* 2*/ ' A',
/* 3*/ ' {',
/* 4*/ ' {',
/* 5*/ ' B',
/* 6*/ ' }',
/* 7*/ ' {',
/* 8*/ ' A',
/* 9*/ ' {',
/* 10*/ ' A',
/* 11*/ ' }',
/* 12*/ ' {',
/* 13*/ ' {',
/* 14*/ ' {',
/* 15*/ ' A',
/* 16*/ ' }',
/* 17*/ ' }',
/* 18*/ ' }',
/* 19*/ ' }',
/* 20*/ ' }',
/* 21*/ '}',
/* 22*/ '{',
/* 23*/ ' A',
/* 24*/ '}',
];
let r1 = r(1, 20); //0
let r2 = r(3, 19); //1
let r3 = r(4, 5); //2
let r4 = r(7, 18); //2
let r5 = r(9, 10); //3
let r6 = r(12, 17); //4
let r7 = r(13, 16); //5
let r8 = r(14, 15); //6
let r9 = r(22, 23); //0
let model = TextModel.createFromString(lines.join('\n'));
let ranges = [r1, r2, r3, r4, r5, r6, r7, r8, r9];
let providers = [new TestFoldingRangeProvider(model, ranges)];
async function assertLimit(maxEntries: number, expectedRanges: IndentRange[], message: string) {
let indentRanges = await new SyntaxRangeProvider(model, providers, maxEntries).compute(CancellationToken.None);
let actual = [];
for (let i = 0; i < indentRanges.length; i++) {
actual.push({ start: indentRanges.getStartLineNumber(i), end: indentRanges.getEndLineNumber(i) });
}
assert.deepEqual(actual, expectedRanges, message);
}
await assertLimit(1000, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '1000');
await assertLimit(9, [r1, r2, r3, r4, r5, r6, r7, r8, r9], '9');
await assertLimit(8, [r1, r2, r3, r4, r5, r6, r7, r9], '8');
await assertLimit(7, [r1, r2, r3, r4, r5, r6, r9], '7');
await assertLimit(6, [r1, r2, r3, r4, r5, r9], '6');
await assertLimit(5, [r1, r2, r3, r4, r9], '5');
await assertLimit(4, [r1, r2, r3, r9], '4');
await assertLimit(3, [r1, r2, r9], '3');
await assertLimit(2, [r1, r9], '2');
await assertLimit(1, [r1], '1');
await assertLimit(0, [], '0');
});
});

View File

@@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { registerEditorAction, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorZoom } from 'vs/editor/common/config/editorZoom';
class EditorFontZoomIn extends EditorAction {
constructor() {
super({
id: 'editor.action.fontZoomIn',
label: nls.localize('EditorFontZoomIn.label', "Editor Font Zoom In"),
alias: 'Editor Font Zoom In',
precondition: null
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
EditorZoom.setZoomLevel(EditorZoom.getZoomLevel() + 1);
}
}
class EditorFontZoomOut extends EditorAction {
constructor() {
super({
id: 'editor.action.fontZoomOut',
label: nls.localize('EditorFontZoomOut.label', "Editor Font Zoom Out"),
alias: 'Editor Font Zoom Out',
precondition: null
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
EditorZoom.setZoomLevel(EditorZoom.getZoomLevel() - 1);
}
}
class EditorFontZoomReset extends EditorAction {
constructor() {
super({
id: 'editor.action.fontZoomReset',
label: nls.localize('EditorFontZoomReset.label', "Editor Font Zoom Reset"),
alias: 'Editor Font Zoom Reset',
precondition: null
});
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
EditorZoom.setZoomLevel(0);
}
}
registerEditorAction(EditorFontZoomIn);
registerEditorAction(EditorFontZoomOut);
registerEditorAction(EditorFontZoomReset);

View File

@@ -3,8 +3,6 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/errors';
import URI from 'vs/base/common/uri';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
@@ -14,7 +12,7 @@ import { ITextModel } from 'vs/editor/common/model';
import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions';
import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import { asWinJsPromise, sequence } from 'vs/base/common/async';
import { asWinJsPromise, first } from 'vs/base/common/async';
import { Position } from 'vs/editor/common/core/position';
export class NoProviderError extends Error {
@@ -36,17 +34,10 @@ export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range,
return TPromise.wrapError(new NoProviderError());
}
let result: TextEdit[];
return sequence(providers.map(provider => {
return () => {
if (!isFalsyOrEmpty(result)) {
return undefined;
}
return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(value => {
result = value;
}, onUnexpectedExternalError);
};
})).then(() => result);
return first(providers.map(provider => () => {
return asWinJsPromise(token => provider.provideDocumentRangeFormattingEdits(model, range, options, token))
.then(undefined, onUnexpectedExternalError);
}), result => !isFalsyOrEmpty(result));
}
export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions): TPromise<TextEdit[]> {
@@ -57,17 +48,10 @@ export function getDocumentFormattingEdits(model: ITextModel, options: Formattin
return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options);
}
let result: TextEdit[];
return sequence(providers.map(provider => {
return () => {
if (!isFalsyOrEmpty(result)) {
return undefined;
}
return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token)).then(value => {
result = value;
}, onUnexpectedExternalError);
};
})).then(() => result);
return first(providers.map(provider => () => {
return asWinJsPromise(token => provider.provideDocumentFormattingEdits(model, options, token))
.then(undefined, onUnexpectedExternalError);
}), result => !isFalsyOrEmpty(result));
}
export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): TPromise<TextEdit[]> {

View File

@@ -14,7 +14,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { registerEditorAction, ServicesAccessor, EditorAction, registerEditorContribution, IActionOptions } from 'vs/editor/browser/editorExtensions';
import { OnTypeFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry } from 'vs/editor/common/modes';
import { getOnTypeFormattingEdits, getDocumentFormattingEdits, getDocumentRangeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format';
import { EditOperationsCommand } from 'vs/editor/contrib/format/formatCommand';
import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
@@ -26,6 +26,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
function alertFormattingEdits(edits: ISingleEditOperation[]): void {
@@ -91,10 +92,10 @@ class FormatOnType implements editorCommon.IEditorContribution {
return;
}
var model = this.editor.getModel();
const model = this.editor.getModel();
// no support
var [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
const [support] = OnTypeFormattingEditProviderRegistry.ordered(model);
if (!support || !support.autoFormatTriggerCharacters) {
return;
}
@@ -118,14 +119,14 @@ class FormatOnType implements editorCommon.IEditorContribution {
return;
}
var model = this.editor.getModel(),
position = this.editor.getPosition(),
canceled = false;
const model = this.editor.getModel();
const position = this.editor.getPosition();
let canceled = false;
// install a listener that checks if edits happens before the
// position on which we format right now. If so, we won't
// apply the format edits
var unbind = this.editor.onDidChangeModelContent((e) => {
const unbind = this.editor.onDidChangeModelContent((e) => {
if (e.isFlush) {
// a model.setValue() was called
// cancel only once
@@ -161,7 +162,7 @@ class FormatOnType implements editorCommon.IEditorContribution {
return;
}
EditOperationsCommand.executeAsCommand(this.editor, edits);
FormattingEdit.execute(this.editor, edits);
alertFormattingEdits(edits);
}, (err) => {
@@ -244,7 +245,7 @@ class FormatOnPaste implements editorCommon.IEditorContribution {
if (!state.validate(this.editor) || isFalsyOrEmpty(edits)) {
return;
}
EditOperationsCommand.execute(this.editor, edits);
FormattingEdit.execute(this.editor, edits);
alertFormattingEdits(edits);
});
}
@@ -280,7 +281,7 @@ export abstract class AbstractFormatAction extends EditorAction {
return;
}
EditOperationsCommand.execute(editor, edits);
FormattingEdit.execute(editor, edits);
alertFormattingEdits(edits);
editor.focus();
}, err => {
@@ -310,7 +311,8 @@ export class FormatDocumentAction extends AbstractFormatAction {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_F,
// secondary: [KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_D)],
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I }
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_I },
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
when: EditorContextKeys.hasDocumentFormattingProvider,
@@ -341,7 +343,8 @@ export class FormatSelectionAction extends AbstractFormatAction {
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasNonEmptySelection),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_F),
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
when: ContextKeyExpr.and(EditorContextKeys.hasDocumentSelectionFormattingProvider, EditorContextKeys.hasNonEmptySelection),

View File

@@ -1,162 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as strings from 'vs/base/common/strings';
import { Range } from 'vs/editor/common/core/range';
import { TextEdit } from 'vs/editor/common/modes';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Selection } from 'vs/editor/common/core/selection';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { EditOperation } from 'vs/editor/common/core/editOperation';
export class EditOperationsCommand implements editorCommon.ICommand {
static _handleEolEdits(editor: ICodeEditor, edits: TextEdit[]): ISingleEditOperation[] {
let newEol: EndOfLineSequence = undefined;
let singleEdits: ISingleEditOperation[] = [];
for (let edit of edits) {
if (typeof edit.eol === 'number') {
newEol = edit.eol;
}
if (edit.range && typeof edit.text === 'string') {
singleEdits.push(edit);
}
}
if (typeof newEol === 'number') {
editor.getModel().setEOL(newEol);
}
return singleEdits;
}
static executeAsCommand(editor: ICodeEditor, _edits: TextEdit[]) {
let edits = this._handleEolEdits(editor, _edits);
const cmd = new EditOperationsCommand(edits, editor.getSelection());
editor.pushUndoStop();
editor.executeCommand('formatEditsCommand', cmd);
editor.pushUndoStop();
}
static isFullModelReplaceEdit(editor: ICodeEditor, edit: ISingleEditOperation): boolean {
const model = editor.getModel();
const editRange = model.validateRange(edit.range);
const fullModelRange = model.getFullModelRange();
return fullModelRange.equalsRange(editRange);
}
static execute(editor: ICodeEditor, _edits: TextEdit[]) {
let edits = this._handleEolEdits(editor, _edits);
editor.pushUndoStop();
if (edits.length === 1 && EditOperationsCommand.isFullModelReplaceEdit(editor, edits[0])) {
// We use replace semantics and hope that markers stay put...
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
} else {
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)));
}
editor.pushUndoStop();
}
private _edits: ISingleEditOperation[];
private _initialSelection: Selection;
private _selectionId: string;
constructor(edits: ISingleEditOperation[], initialSelection: Selection) {
this._initialSelection = initialSelection;
this._edits = edits;
}
public getEditOperations(model: ITextModel, builder: editorCommon.IEditOperationBuilder): void {
for (let edit of this._edits) {
// We know that this edit.range comes from the mirror model, so it should only contain \n and no \r's
let trimEdit = EditOperationsCommand.trimEdit(edit, model);
if (trimEdit !== null) { // produced above in case the edit.text is identical to the existing text
builder.addEditOperation(Range.lift(edit.range), edit.text);
}
}
var selectionIsSet = false;
if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
if (this._edits[0].range.startColumn === this._initialSelection.endColumn &&
this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
selectionIsSet = true;
this._selectionId = builder.trackSelection(this._initialSelection, true);
} else if (this._edits[0].range.endColumn === this._initialSelection.startColumn &&
this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
selectionIsSet = true;
this._selectionId = builder.trackSelection(this._initialSelection, false);
}
}
if (!selectionIsSet) {
this._selectionId = builder.trackSelection(this._initialSelection);
}
}
public computeCursorState(model: ITextModel, helper: editorCommon.ICursorStateComputerData): Selection {
return helper.getTrackedSelection(this._selectionId);
}
static fixLineTerminators(edit: ISingleEditOperation, model: ITextModel): void {
edit.text = edit.text.replace(/\r\n|\r|\n/g, model.getEOL());
}
/**
* This is used to minimize the edits by removing changes that appear on the edges of the range which are identical
* to the current text.
*
* The reason this was introduced is to allow better selection tracking of the current cursor and solve
* bug #15108. There the cursor was jumping since the tracked selection was in the middle of the range edit
* and was lost.
*/
static trimEdit(edit: ISingleEditOperation, model: ITextModel): ISingleEditOperation {
this.fixLineTerminators(edit, model);
return this._trimEdit(model.validateRange(edit.range), edit.text, edit.forceMoveMarkers, model);
}
static _trimEdit(editRange: Range, editText: string, editForceMoveMarkers: boolean, model: ITextModel): ISingleEditOperation {
let currentText = model.getValueInRange(editRange);
// Find the equal characters in the front
let commonPrefixLength = strings.commonPrefixLength(editText, currentText);
// If the two strings are identical, return no edit (no-op)
if (commonPrefixLength === currentText.length && commonPrefixLength === editText.length) {
return null;
}
if (commonPrefixLength > 0) {
// Apply front trimming
let newStartPosition = model.modifyPosition(editRange.getStartPosition(), commonPrefixLength);
editRange = new Range(newStartPosition.lineNumber, newStartPosition.column, editRange.endLineNumber, editRange.endColumn);
editText = editText.substring(commonPrefixLength);
currentText = currentText.substr(commonPrefixLength);
}
// Find the equal characters in the rear
let commonSuffixLength = strings.commonSuffixLength(editText, currentText);
if (commonSuffixLength > 0) {
// Apply rear trimming
let newEndPosition = model.modifyPosition(editRange.getEndPosition(), -commonSuffixLength);
editRange = new Range(editRange.startLineNumber, editRange.startColumn, newEndPosition.lineNumber, newEndPosition.column);
editText = editText.substring(0, editText.length - commonSuffixLength);
currentText = currentText.substring(0, currentText.length - commonSuffixLength);
}
return {
text: editText,
range: editRange,
forceMoveMarkers: editForceMoveMarkers
};
}
}

View File

@@ -0,0 +1,53 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { EndOfLineSequence, ISingleEditOperation } from 'vs/editor/common/model';
import { TextEdit } from 'vs/editor/common/modes';
export class FormattingEdit {
private static _handleEolEdits(editor: ICodeEditor, edits: TextEdit[]): ISingleEditOperation[] {
let newEol: EndOfLineSequence = undefined;
let singleEdits: ISingleEditOperation[] = [];
for (let edit of edits) {
if (typeof edit.eol === 'number') {
newEol = edit.eol;
}
if (edit.range && typeof edit.text === 'string') {
singleEdits.push(edit);
}
}
if (typeof newEol === 'number') {
editor.getModel().pushEOL(newEol);
}
return singleEdits;
}
private static _isFullModelReplaceEdit(editor: ICodeEditor, edit: ISingleEditOperation): boolean {
const model = editor.getModel();
const editRange = model.validateRange(edit.range);
const fullModelRange = model.getFullModelRange();
return fullModelRange.equalsRange(editRange);
}
static execute(editor: ICodeEditor, _edits: TextEdit[]) {
editor.pushUndoStop();
let edits = FormattingEdit._handleEolEdits(editor, _edits);
if (edits.length === 1 && FormattingEdit._isFullModelReplaceEdit(editor, edits[0])) {
// We use replace semantics and hope that markers stay put...
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text)));
} else {
editor.executeEdits('formatEditsCommand', edits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text)));
}
editor.pushUndoStop();
}
}

View File

@@ -1,362 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as assert from 'assert';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ISingleEditOperation } from 'vs/editor/common/model';
import { TextModel } from 'vs/editor/common/model/textModel';
import { EditOperationsCommand } from 'vs/editor/contrib/format/formatCommand';
import { testCommand } from 'vs/editor/test/browser/testCommand';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
function editOp(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, text: string[]): ISingleEditOperation {
return {
range: new Range(startLineNumber, startColumn, endLineNumber, endColumn),
text: text.join('\n'),
forceMoveMarkers: false
};
}
suite('FormatCommand.trimEdit', () => {
function testTrimEdit(lines: string[], edit: ISingleEditOperation, expected: ISingleEditOperation): void {
let model = TextModel.createFromString(lines.join('\n'));
let actual = EditOperationsCommand.trimEdit(edit, model);
assert.deepEqual(actual, expected);
model.dispose();
}
test('single-line no-op', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 1, 10, [
'some text'
]),
null
);
});
test('multi-line no-op 1', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 2, 16, [
'some text',
'some other text'
]),
null
);
});
test('multi-line no-op 2', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 2, 1, [
'some text',
''
]),
null
);
});
test('simple prefix, no suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 1, 10, [
'some interesting thing'
]),
editOp(1, 6, 1, 10, [
'interesting thing'
])
);
});
test('whole line prefix, no suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 1, 10, [
'some text',
'interesting thing'
]),
editOp(1, 10, 1, 10, [
'',
'interesting thing'
])
);
});
test('multi-line prefix, no suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 2, 16, [
'some text',
'some other interesting thing'
]),
editOp(2, 12, 2, 16, [
'interesting thing'
])
);
});
test('no prefix, simple suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 1, 10, [
'interesting text'
]),
editOp(1, 1, 1, 5, [
'interesting'
])
);
});
test('no prefix, whole line suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 1, 10, [
'interesting thing',
'some text'
]),
editOp(1, 1, 1, 1, [
'interesting thing',
''
])
);
});
test('no prefix, multi-line suffix', () => {
testTrimEdit(
[
'some text',
'some other text'
],
editOp(1, 1, 2, 16, [
'interesting thing text',
'some other text'
]),
editOp(1, 1, 1, 5, [
'interesting thing'
])
);
});
test('no overlapping prefix & suffix', () => {
testTrimEdit(
[
'some cool text'
],
editOp(1, 1, 1, 15, [
'some interesting text'
]),
editOp(1, 6, 1, 10, [
'interesting'
])
);
});
test('overlapping prefix & suffix 1', () => {
testTrimEdit(
[
'some cool text'
],
editOp(1, 1, 1, 15, [
'some cool cool text'
]),
editOp(1, 11, 1, 11, [
'cool '
])
);
});
test('overlapping prefix & suffix 2', () => {
testTrimEdit(
[
'some cool cool text'
],
editOp(1, 1, 1, 29, [
'some cool text'
]),
editOp(1, 11, 1, 16, [
''
])
);
});
});
suite('FormatCommand', () => {
function testFormatCommand(lines: string[], selection: Selection, edits: ISingleEditOperation[], expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, null, selection, (sel) => new EditOperationsCommand(edits, sel), expectedLines, expectedSelection);
}
test('no-op', () => {
testFormatCommand(
[
'some text',
'some other text'
],
new Selection(2, 1, 2, 5),
[
editOp(1, 1, 2, 16, [
'some text',
'some other text'
])
],
[
'some text',
'some other text'
],
new Selection(2, 1, 2, 5)
);
});
test('trim beginning', () => {
testFormatCommand(
[
'some text',
'some other text'
],
new Selection(2, 1, 2, 5),
[
editOp(1, 1, 2, 16, [
'some text',
'some new other text'
])
],
[
'some text',
'some new other text'
],
new Selection(2, 1, 2, 5)
);
});
test('issue #144', () => {
testFormatCommand(
[
'package caddy',
'',
'func main() {',
'\tfmt.Println("Hello World! :)")',
'}',
''
],
new Selection(1, 1, 1, 1),
[
editOp(1, 1, 6, 1, [
'package caddy',
'',
'import "fmt"',
'',
'func main() {',
'\tfmt.Println("Hello World! :)")',
'}',
''
])
],
[
'package caddy',
'',
'import "fmt"',
'',
'func main() {',
'\tfmt.Println("Hello World! :)")',
'}',
''
],
new Selection(1, 1, 1, 1)
);
});
test('issue #23765', () => {
testFormatCommand(
[
' let a;'
],
new Selection(1, 1, 1, 1),
[
editOp(1, 1, 1, 2, [
''
])
],
[
'let a;'
],
new Selection(1, 1, 1, 1)
);
});
test('issue #44870', () => {
const initialText = [
'[',
' {},{',
' }',
']',
];
withTestCodeEditor(initialText, {}, (editor) => {
editor.setSelection(new Selection(2, 8, 2, 8));
EditOperationsCommand.execute(editor, [
editOp(2, 8, 2, 8, ['', ' ']),
editOp(2, 9, 3, 5, ['']),
]);
assert.equal(editor.getValue(), [
'[',
' {},',
' {}',
']',
].join('\n'));
assert.deepEqual(editor.getSelection(), new Selection(3, 5, 3, 5));
});
});
test('issue #47382: full model replace moves cursor to end of file', () => {
const initialText = [
'just some',
'Text',
'...more text'
];
withTestCodeEditor(initialText, {}, (editor) => {
editor.setSelection(new Selection(2, 1, 2, 1));
EditOperationsCommand.execute(editor, [{
range: new Range(1, 1, 3, 13),
text: [
'just some',
'\tText',
'...more text'
].join('\n')
}]);
assert.equal(editor.getValue(), [
'just some',
'\tText',
'...more text'
].join('\n'));
assert.deepEqual(editor.getSelection(), new Selection(2, 1, 2, 1));
});
});
});

View File

@@ -5,7 +5,7 @@
'use strict';
import 'vs/css!./goToDeclarationMouse';
import 'vs/css!./goToDefinitionMouse';
import { KeyCode } from 'vs/base/common/keyCodes';
import * as browser from 'vs/base/browser/browser';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
@@ -52,19 +52,20 @@ export class ClickLinkKeyboardEvent {
this.hasTriggerModifier = hasModifier(source, opts.triggerModifier);
}
}
export type TriggerModifier = 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey';
export class ClickLinkOptions {
public readonly triggerKey: KeyCode;
public readonly triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey';
public readonly triggerModifier: TriggerModifier;
public readonly triggerSideBySideKey: KeyCode;
public readonly triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey';
public readonly triggerSideBySideModifier: TriggerModifier;
constructor(
triggerKey: KeyCode,
triggerModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey',
triggerModifier: TriggerModifier,
triggerSideBySideKey: KeyCode,
triggerSideBySideModifier: 'ctrlKey' | 'shiftKey' | 'altKey' | 'metaKey'
triggerSideBySideModifier: TriggerModifier
) {
this.triggerKey = triggerKey;
this.triggerModifier = triggerModifier;

View File

@@ -3,29 +3,27 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { flatten, coalesce } from 'vs/base/common/arrays';
import { asWinJsPromise } from 'vs/base/common/async';
import { CancellationToken } from 'vs/base/common/cancellation';
import { onUnexpectedExternalError } from 'vs/base/common/errors';
import { TPromise } from 'vs/base/common/winjs.base';
import { ITextModel } from 'vs/editor/common/model';
import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions';
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
import { DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, Location } from 'vs/editor/common/modes';
import { CancellationToken } from 'vs/base/common/cancellation';
import { asWinJsPromise } from 'vs/base/common/async';
import { Position } from 'vs/editor/common/core/position';
import { flatten } from 'vs/base/common/arrays';
import { ITextModel } from 'vs/editor/common/model';
import { DefinitionLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry } from 'vs/editor/common/modes';
import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegistry';
function getDefinitions<T>(
model: ITextModel,
position: Position,
registry: LanguageFeatureRegistry<T>,
provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => Location | Location[] | Thenable<Location | Location[]>
): TPromise<Location[]> {
provide: (provider: T, model: ITextModel, position: Position, token: CancellationToken) => DefinitionLink | DefinitionLink[] | Thenable<DefinitionLink | DefinitionLink[]>
): TPromise<DefinitionLink[]> {
const provider = registry.ordered(model);
// get results
const promises = provider.map((provider, idx): TPromise<Location | Location[]> => {
const promises = provider.map((provider): TPromise<DefinitionLink | DefinitionLink[]> => {
return asWinJsPromise((token) => {
return provide(provider, model, position, token);
}).then(undefined, err => {
@@ -35,23 +33,23 @@ function getDefinitions<T>(
});
return TPromise.join(promises)
.then(flatten)
.then(references => references.filter(x => !!x));
.then(references => coalesce(references));
}
export function getDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<Location[]> {
export function getDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, DefinitionProviderRegistry, (provider, model, position, token) => {
return provider.provideDefinition(model, position, token);
});
}
export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise<Location[]> {
export function getImplementationsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, ImplementationProviderRegistry, (provider, model, position, token) => {
return provider.provideImplementation(model, position, token);
});
}
export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<Location[]> {
export function getTypeDefinitionsAtPosition(model: ITextModel, position: Position): TPromise<DefinitionLink[]> {
return getDefinitions(model, position, TypeDefinitionProviderRegistry, (provider, model, position, token) => {
return provider.provideTypeDefinition(model, position, token);
});

View File

@@ -3,18 +3,16 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import { alert } from 'vs/base/browser/ui/aria/aria';
import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes';
import * as platform from 'vs/base/common/platform';
import { TPromise } from 'vs/base/common/winjs.base';
import { IEditorService } from 'vs/platform/editor/common/editor';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { Range } from 'vs/editor/common/core/range';
import { registerEditorAction, IActionOptions, ServicesAccessor, EditorAction } from 'vs/editor/browser/editorExtensions';
import { Location } from 'vs/editor/common/modes';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDeclaration';
import { DefinitionLink } from 'vs/editor/common/modes';
import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition } from './goToDefinition';
import { ReferencesController } from 'vs/editor/contrib/referenceSearch/referencesController';
import { ReferencesModel } from 'vs/editor/contrib/referenceSearch/referencesModel';
import { PeekContext } from 'vs/editor/contrib/referenceSearch/peekViewWidget';
@@ -26,6 +24,8 @@ import { IProgressService } from 'vs/platform/progress/common/progress';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { createCancelablePromise } from 'vs/base/common/async';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
export class DefinitionActionConfig {
@@ -50,7 +50,7 @@ export class DefinitionAction extends EditorAction {
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const notificationService = accessor.get(INotificationService);
const editorService = accessor.get(IEditorService);
const editorService = accessor.get(ICodeEditorService);
const progressService = accessor.get(IProgressService);
const model = editor.getModel();
@@ -66,7 +66,7 @@ export class DefinitionAction extends EditorAction {
// * remove falsy references
// * find reference at the current pos
let idxOfCurrent = -1;
let result: Location[] = [];
const result: DefinitionLink[] = [];
for (let i = 0; i < references.length; i++) {
let reference = references[i];
if (!reference || !reference.range) {
@@ -95,7 +95,7 @@ export class DefinitionAction extends EditorAction {
} else if (result.length === 1 && idxOfCurrent !== -1) {
// only the position at which we are -> adjust selection
let [current] = result;
this._openReference(editorService, current, false);
this._openReference(editor, editorService, current, false);
} else {
// handle multile results
@@ -111,7 +111,7 @@ export class DefinitionAction extends EditorAction {
return definitionPromise;
}
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<Location[]> {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getDefinitionsAtPosition(model, position);
}
@@ -125,7 +125,7 @@ export class DefinitionAction extends EditorAction {
return model.references.length > 1 && nls.localize('meta.title', " {0} definitions", model.references.length);
}
private _onResult(editorService: IEditorService, editor: ICodeEditor, model: ReferencesModel) {
private _onResult(editorService: ICodeEditorService, editor: ICodeEditor, model: ReferencesModel) {
const msg = model.getAriaMessage();
alert(msg);
@@ -134,7 +134,7 @@ export class DefinitionAction extends EditorAction {
this._openInPeek(editorService, editor, model);
} else {
let next = model.nearestReference(editor.getModel().uri, editor.getPosition());
this._openReference(editorService, next, this._configuration.openToSide).then(editor => {
this._openReference(editor, editorService, next, this._configuration.openToSide).then(editor => {
if (editor && model.references.length > 1) {
this._openInPeek(editorService, editor, model);
} else {
@@ -144,30 +144,28 @@ export class DefinitionAction extends EditorAction {
}
}
private _openReference(editorService: IEditorService, reference: Location, sideBySide: boolean): TPromise<ICodeEditor> {
let { uri, range } = reference;
return editorService.openEditor({
private _openReference(editor: ICodeEditor, editorService: ICodeEditorService, reference: DefinitionLink, sideBySide: boolean): TPromise<ICodeEditor> {
const { uri, range } = reference;
return editorService.openCodeEditor({
resource: uri,
options: {
selection: Range.collapseToStart(range),
revealIfVisible: true,
revealIfOpened: true,
revealInCenterIfOutsideViewport: true
}
}, sideBySide).then(editor => {
return editor && <ICodeEditor>editor.getControl();
});
}, editor, sideBySide);
}
private _openInPeek(editorService: IEditorService, target: ICodeEditor, model: ReferencesModel) {
private _openInPeek(editorService: ICodeEditorService, target: ICodeEditor, model: ReferencesModel) {
let controller = ReferencesController.get(target);
if (controller) {
controller.toggleWidget(target.getSelection(), TPromise.as(model), {
controller.toggleWidget(target.getSelection(), createCancelablePromise(_ => Promise.resolve(model)), {
getMetaTitle: (model) => {
return this._getMetaTitle(model);
},
onGoto: (reference) => {
controller.closeWidget();
return this._openReference(editorService, reference, false);
return this._openReference(target, editorService, reference, false);
}
});
} else {
@@ -194,7 +192,8 @@ export class GoToDefinitionAction extends DefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: goToDeclarationKb
primary: goToDeclarationKb,
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
group: 'navigation',
@@ -218,7 +217,8 @@ export class OpenDefinitionToSideAction extends DefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb)
primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, goToDeclarationKb),
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -237,7 +237,8 @@ export class PeekDefinitionAction extends DefinitionAction {
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.Alt | KeyCode.F12,
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 }
linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F10 },
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
group: 'navigation',
@@ -248,7 +249,7 @@ export class PeekDefinitionAction extends DefinitionAction {
}
export class ImplementationAction extends DefinitionAction {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<Location[]> {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getImplementationsAtPosition(model, position);
}
@@ -277,7 +278,8 @@ export class GoToImplementationAction extends ImplementationAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.CtrlCmd | KeyCode.F12
primary: KeyMod.CtrlCmd | KeyCode.F12,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -297,14 +299,15 @@ export class PeekImplementationAction extends ImplementationAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.F12,
weight: KeybindingWeight.EditorContrib
}
});
}
}
export class TypeDefinitionAction extends DefinitionAction {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<Location[]> {
protected _getDeclarationsAtPosition(model: ITextModel, position: corePosition.Position): TPromise<DefinitionLink[]> {
return getTypeDefinitionsAtPosition(model, position);
}
@@ -319,13 +322,13 @@ export class TypeDefinitionAction extends DefinitionAction {
}
}
export class GoToTypeDefintionAction extends TypeDefinitionAction {
export class GoToTypeDefinitionAction extends TypeDefinitionAction {
public static readonly ID = 'editor.action.goToTypeDefinition';
constructor() {
super(new DefinitionActionConfig(), {
id: GoToTypeDefintionAction.ID,
id: GoToTypeDefinitionAction.ID,
label: nls.localize('actions.goToTypeDefinition.label', "Go to Type Definition"),
alias: 'Go to Type Definition',
precondition: ContextKeyExpr.and(
@@ -333,7 +336,8 @@ export class GoToTypeDefintionAction extends TypeDefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: 0
primary: 0,
weight: KeybindingWeight.EditorContrib
},
menuOpts: {
group: 'navigation',
@@ -357,7 +361,8 @@ export class PeekTypeDefinitionAction extends TypeDefinitionAction {
EditorContextKeys.isInEmbeddedEditor.toNegated()),
kbOpts: {
kbExpr: EditorContextKeys.editorTextFocus,
primary: 0
primary: 0,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -368,5 +373,5 @@ registerEditorAction(OpenDefinitionToSideAction);
registerEditorAction(PeekDefinitionAction);
registerEditorAction(GoToImplementationAction);
registerEditorAction(PeekImplementationAction);
registerEditorAction(GoToTypeDefintionAction);
registerEditorAction(GoToTypeDefinitionAction);
registerEditorAction(PeekTypeDefinitionAction);

View File

@@ -5,7 +5,7 @@
'use strict';
import 'vs/css!./goToDeclarationMouse';
import 'vs/css!./goToDefinitionMouse';
import * as nls from 'vs/nls';
import { Throttler } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -14,18 +14,19 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { IModeService } from 'vs/editor/common/services/modeService';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Location, DefinitionProviderRegistry } from 'vs/editor/common/modes';
import { DefinitionProviderRegistry, DefinitionLink } from 'vs/editor/common/modes';
import { ICodeEditor, IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { getDefinitionsAtPosition } from './goToDeclaration';
import { getDefinitionsAtPosition } from './goToDefinition';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorActiveLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { EditorState, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState';
import { DefinitionAction, DefinitionActionConfig } from './goToDeclarationCommands';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDeclaration/clickLinkGesture';
import { IWordAtPosition, IModelDeltaDecoration } from 'vs/editor/common/model';
import { DefinitionAction, DefinitionActionConfig } from './goToDefinitionCommands';
import { ClickLinkGesture, ClickLinkMouseEvent, ClickLinkKeyboardEvent } from 'vs/editor/contrib/goToDefinition/clickLinkGesture';
import { IWordAtPosition, IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model';
import { Position } from 'vs/editor/common/core/position';
class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorContribution {
@@ -102,7 +103,7 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
this.throttler.queue(() => {
return state.validate(this.editor)
? this.findDefinition(mouseEvent.target)
: TPromise.wrap<Location[]>(null);
: TPromise.wrap<DefinitionLink[]>(null);
}).then(results => {
if (!results || !results.length || !state.validate(this.editor)) {
@@ -141,25 +142,18 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
return;
}
const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber);
const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES);
let endLineNumber = startLineNumber + 1;
let minIndent = startIndent;
const previewValue = this.getPreviewValue(textEditorModel, startLineNumber);
for (; endLineNumber < maxLineNumber; endLineNumber++) {
let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber);
minIndent = Math.min(minIndent, endIndent);
if (startIndent === endIndent) {
break;
}
let wordRange: Range;
if (result.origin) {
wordRange = Range.lift(result.origin);
} else {
wordRange = new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn);
}
const previewRange = new Range(startLineNumber, 1, endLineNumber + 1, 1);
const value = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim();
this.addDecoration(
new Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn),
new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), value)
wordRange,
new MarkdownString().appendCodeblock(this.modeService.getModeIdByFilenameOrFirstLine(textEditorModel.uri.fsPath), previewValue)
);
ref.dispose();
});
@@ -167,6 +161,92 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
}).done(undefined, onUnexpectedError);
}
private getPreviewValue(textEditorModel: ITextModel, startLineNumber: number) {
let rangeToUse = this.getPreviewRangeBasedOnBrackets(textEditorModel, startLineNumber);
const numberOfLinesInRange = rangeToUse.endLineNumber - rangeToUse.startLineNumber;
if (numberOfLinesInRange >= GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES) {
rangeToUse = this.getPreviewRangeBasedOnIndentation(textEditorModel, startLineNumber);
}
const previewValue = this.stripIndentationFromPreviewRange(textEditorModel, startLineNumber, rangeToUse);
return previewValue;
}
private stripIndentationFromPreviewRange(textEditorModel: ITextModel, startLineNumber: number, previewRange: Range) {
const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber);
let minIndent = startIndent;
for (let endLineNumber = startLineNumber + 1; endLineNumber < previewRange.endLineNumber; endLineNumber++) {
const endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber);
minIndent = Math.min(minIndent, endIndent);
}
const previewValue = textEditorModel.getValueInRange(previewRange).replace(new RegExp(`^\\s{${minIndent - 1}}`, 'gm'), '').trim();
return previewValue;
}
private getPreviewRangeBasedOnIndentation(textEditorModel: ITextModel, startLineNumber: number) {
const startIndent = textEditorModel.getLineFirstNonWhitespaceColumn(startLineNumber);
const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES);
let endLineNumber = startLineNumber + 1;
for (; endLineNumber < maxLineNumber; endLineNumber++) {
let endIndent = textEditorModel.getLineFirstNonWhitespaceColumn(endLineNumber);
if (startIndent === endIndent) {
break;
}
}
return new Range(startLineNumber, 1, endLineNumber + 1, 1);
}
private getPreviewRangeBasedOnBrackets(textEditorModel: ITextModel, startLineNumber: number) {
const maxLineNumber = Math.min(textEditorModel.getLineCount(), startLineNumber + GotoDefinitionWithMouseEditorContribution.MAX_SOURCE_PREVIEW_LINES);
const brackets = [];
let ignoreFirstEmpty = true;
let currentBracket = textEditorModel.findNextBracket(new Position(startLineNumber, 1));
while (currentBracket !== null) {
if (brackets.length === 0) {
brackets.push(currentBracket);
} else {
const lastBracket = brackets[brackets.length - 1];
if (lastBracket.open === currentBracket.open && lastBracket.isOpen && !currentBracket.isOpen) {
brackets.pop();
} else {
brackets.push(currentBracket);
}
if (brackets.length === 0) {
if (ignoreFirstEmpty) {
ignoreFirstEmpty = false;
} else {
return new Range(startLineNumber, 1, currentBracket.range.endLineNumber + 1, 1);
}
}
}
const maxColumn = textEditorModel.getLineMaxColumn(startLineNumber);
let nextLineNumber = currentBracket.range.endLineNumber;
let nextColumn = currentBracket.range.endColumn;
if (maxColumn === currentBracket.range.endColumn) {
nextLineNumber++;
nextColumn = 1;
}
if (nextLineNumber > maxLineNumber) {
return new Range(startLineNumber, 1, maxLineNumber + 1, 1);
}
currentBracket = textEditorModel.findNextBracket(new Position(nextLineNumber, nextColumn));
}
return new Range(startLineNumber, 1, maxLineNumber + 1, 1);
}
private addDecoration(range: Range, hoverMessage: MarkdownString): void {
const newDecorations: IModelDeltaDecoration = {
@@ -194,13 +274,13 @@ class GotoDefinitionWithMouseEditorContribution implements editorCommon.IEditorC
DefinitionProviderRegistry.has(this.editor.getModel());
}
private findDefinition(target: IMouseTarget): TPromise<Location[]> {
let model = this.editor.getModel();
private findDefinition(target: IMouseTarget): TPromise<DefinitionLink[]> {
const model = this.editor.getModel();
if (!model) {
return TPromise.as(null);
}
return getDefinitionsAtPosition(this.editor.getModel(), target.position);
return getDefinitionsAtPosition(model, target.position);
}
private gotoDefinition(target: IMouseTarget, sideBySide: boolean): TPromise<any> {

View File

@@ -16,14 +16,14 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { registerEditorAction, registerEditorContribution, ServicesAccessor, IActionOptions, EditorAction, EditorCommand, registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { MarkerNavigationWidget } from './gotoErrorWidget';
import { compare } from 'vs/base/common/strings';
import { binarySearch } from 'vs/base/common/arrays';
import { IEditorService } from 'vs/platform/editor/common/editor';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { TPromise } from 'vs/base/common/winjs.base';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -202,7 +202,7 @@ class MarkerController implements editorCommon.IEditorContribution {
@IMarkerService private readonly _markerService: IMarkerService,
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@IThemeService private readonly _themeService: IThemeService,
@IEditorService private readonly _editorService: IEditorService
@ICodeEditorService private readonly _editorService: ICodeEditorService
) {
this._editor = editor;
this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService);
@@ -239,10 +239,10 @@ class MarkerController implements editorCommon.IEditorContribution {
this._disposeOnClose.push(this._model);
this._disposeOnClose.push(this._widget);
this._disposeOnClose.push(this._widget.onDidSelectRelatedInformation(related => {
this._editorService.openEditor({
this._editorService.openCodeEditor({
resource: related.resource,
options: { pinned: true, revealIfOpened: true, selection: Range.lift(related).collapseToStart() }
}).then(undefined, onUnexpectedError);
}, this._editor).then(undefined, onUnexpectedError);
this.closeMarkersNavigation(false);
}));
this._disposeOnClose.push(this._editor.onDidChangeModel(() => this._cleanUp()));
@@ -294,23 +294,26 @@ class MarkerNavigationAction extends EditorAction {
private _isNext: boolean;
constructor(next: boolean, opts: IActionOptions) {
private _multiFile: boolean;
constructor(next: boolean, multiFile: boolean, opts: IActionOptions) {
super(opts);
this._isNext = next;
this._multiFile = multiFile;
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): TPromise<void> {
const markerService = accessor.get(IMarkerService);
const editorService = accessor.get(IEditorService);
const editorService = accessor.get(ICodeEditorService);
const controller = MarkerController.get(editor);
if (!controller) {
return undefined;
}
const model = controller.getOrCreateModel();
const atEdge = model.move(this._isNext, false);
if (!atEdge) {
const atEdge = model.move(this._isNext, !this._multiFile);
if (!atEdge || !this._multiFile) {
return undefined;
}
@@ -344,14 +347,14 @@ class MarkerNavigationAction extends EditorAction {
// for the next marker and re-start marker navigation in there
controller.closeMarkersNavigation();
return editorService.openEditor({
return editorService.openCodeEditor({
resource: newMarker.resource,
options: { pinned: false, revealIfOpened: true, revealInCenterIfOutsideViewport: true, selection: newMarker }
}).then(editor => {
if (!editor || !isCodeEditor(editor.getControl())) {
}, editor).then(editor => {
if (!editor) {
return undefined;
}
return (<ICodeEditor>editor.getControl()).getAction(this.id).run();
return editor.getAction(this.id).run();
});
}
@@ -369,29 +372,53 @@ class MarkerNavigationAction extends EditorAction {
class NextMarkerAction extends MarkerNavigationAction {
constructor() {
super(true, {
super(true, false, {
id: 'editor.action.marker.next',
label: nls.localize('markerAction.next.label', "Go to Next Problem (Error, Warning, Info)"),
alias: 'Go to Next Error or Warning',
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyCode.F8
}
precondition: EditorContextKeys.writable
});
}
}
class PrevMarkerAction extends MarkerNavigationAction {
constructor() {
super(false, {
super(false, false, {
id: 'editor.action.marker.prev',
label: nls.localize('markerAction.previous.label', "Go to Previous Problem (Error, Warning, Info)"),
alias: 'Go to Previous Error or Warning',
precondition: EditorContextKeys.writable
});
}
}
class NextMarkerInFilesAction extends MarkerNavigationAction {
constructor() {
super(true, true, {
id: 'editor.action.marker.nextInFiles',
label: nls.localize('markerAction.nextInFiles.label', "Go to Next Problem in Files (Error, Warning, Info)"),
alias: 'Go to Next Error or Warning in Files',
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Shift | KeyCode.F8
primary: KeyCode.F8,
weight: KeybindingWeight.EditorContrib
}
});
}
}
class PrevMarkerInFilesAction extends MarkerNavigationAction {
constructor() {
super(false, true, {
id: 'editor.action.marker.prevInFiles',
label: nls.localize('markerAction.previousInFiles.label', "Go to Previous Problem in Files (Error, Warning, Info)"),
alias: 'Go to Previous Error or Warning in Files',
precondition: EditorContextKeys.writable,
kbOpts: {
kbExpr: EditorContextKeys.focus,
primary: KeyMod.Shift | KeyCode.F8,
weight: KeybindingWeight.EditorContrib
}
});
}
@@ -400,6 +427,8 @@ class PrevMarkerAction extends MarkerNavigationAction {
registerEditorContribution(MarkerController);
registerEditorAction(NextMarkerAction);
registerEditorAction(PrevMarkerAction);
registerEditorAction(NextMarkerInFilesAction);
registerEditorAction(PrevMarkerInFilesAction);
const CONTEXT_MARKERS_NAVIGATION_VISIBLE = new RawContextKey<boolean>('markersNavigationVisible', false);
@@ -410,7 +439,7 @@ registerEditorCommand(new MarkerCommand({
precondition: CONTEXT_MARKERS_NAVIGATION_VISIBLE,
handler: x => x.closeMarkersNavigation(),
kbOpts: {
weight: KeybindingsRegistry.WEIGHT.editorContrib(50),
weight: KeybindingWeight.EditorContrib + 50,
kbExpr: EditorContextKeys.focus,
primary: KeyCode.Escape,
secondary: [KeyMod.Shift | KeyCode.Escape]

Some files were not shown because too many files have changed in this diff Show More