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

File diff suppressed because it is too large Load Diff

View File

@@ -61,6 +61,8 @@ export interface IEnvConfiguration {
accessibilitySupport: platform.AccessibilitySupport;
}
const hasOwnProperty = Object.hasOwnProperty;
export abstract class CommonEditorConfiguration extends Disposable implements editorCommon.IConfiguration {
protected _rawOptions: editorOptions.IEditorOptions;
@@ -80,6 +82,7 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
this._rawOptions.scrollbar = objects.mixin({}, this._rawOptions.scrollbar || {});
this._rawOptions.minimap = objects.mixin({}, this._rawOptions.minimap || {});
this._rawOptions.find = objects.mixin({}, this._rawOptions.find || {});
this._rawOptions.hover = objects.mixin({}, this._rawOptions.hover || {});
this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS);
this.editor = null;
@@ -90,6 +93,9 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
this._register(TabFocus.onDidChangeTabFocus(_ => this._recomputeOptions()));
}
public observeReferenceElement(dimension?: editorCommon.IDimension): void {
}
public dispose(): void {
super.dispose();
}
@@ -132,7 +138,53 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
return editorOptions.InternalEditorOptionsFactory.createInternalEditorOptions(env, opts);
}
private static _primitiveArrayEquals(a: any[], b: any[]): boolean {
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
}
private static _subsetEquals(base: object, subset: object): boolean {
for (let key in subset) {
if (hasOwnProperty.call(subset, key)) {
const subsetValue = subset[key];
const baseValue = base[key];
if (baseValue === subsetValue) {
continue;
}
if (Array.isArray(baseValue) && Array.isArray(subsetValue)) {
if (!this._primitiveArrayEquals(baseValue, subsetValue)) {
return false;
}
continue;
}
if (typeof baseValue === 'object' && typeof subsetValue === 'object') {
if (!this._subsetEquals(baseValue, subsetValue)) {
return false;
}
continue;
}
return false;
}
}
return true;
}
public updateOptions(newOptions: editorOptions.IEditorOptions): void {
if (typeof newOptions === 'undefined') {
return;
}
if (CommonEditorConfiguration._subsetEquals(this._rawOptions, newOptions)) {
return;
}
this._rawOptions = objects.mixin(this._rawOptions, newOptions || {});
this._validatedOptions = editorOptions.EditorOptionsValidator.validate(this._rawOptions, EDITOR_DEFAULTS);
this._recomputeOptions();
@@ -194,7 +246,7 @@ const editorConfiguration: IConfigurationNode = {
'editor.lineHeight': {
'type': 'number',
'default': EDITOR_FONT_DEFAULTS.lineHeight,
'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the lineHeight from the fontSize.")
'description': nls.localize('lineHeight', "Controls the line height. Use 0 to compute the line height from the font size.")
},
'editor.letterSpacing': {
'type': 'number',
@@ -219,50 +271,55 @@ const editorConfiguration: IConfigurationNode = {
'type': 'number'
},
'default': EDITOR_DEFAULTS.viewInfo.rulers,
'description': nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty")
'description': nls.localize('rulers', "Render vertical rulers after a certain number of monospace characters. Use multiple values for multiple rulers. No rulers are drawn if array is empty.")
},
'editor.wordSeparators': {
'type': 'string',
'default': EDITOR_DEFAULTS.wordSeparators,
'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations")
'description': nls.localize('wordSeparators', "Characters that will be used as word separators when doing word related navigations or operations.")
},
'editor.tabSize': {
'type': 'number',
'default': EDITOR_MODEL_DEFAULTS.tabSize,
'minimum': 1,
'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `editor.detectIndentation` is on."),
'description': nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."),
'errorMessage': nls.localize('tabSize.errorMessage', "Expected 'number'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
},
'editor.insertSpaces': {
'type': 'boolean',
'default': EDITOR_MODEL_DEFAULTS.insertSpaces,
'description': nls.localize('insertSpaces', "Insert spaces when pressing Tab. This setting is overridden based on the file contents when `editor.detectIndentation` is on."),
'description': nls.localize('insertSpaces', "Insert spaces when pressing `Tab`. This setting is overridden based on the file contents when `#editor.detectIndentation#` is on."),
'errorMessage': nls.localize('insertSpaces.errorMessage', "Expected 'boolean'. Note that the value \"auto\" has been replaced by the `editor.detectIndentation` setting.")
},
'editor.detectIndentation': {
'type': 'boolean',
'default': EDITOR_MODEL_DEFAULTS.detectIndentation,
'description': nls.localize('detectIndentation', "When opening a file, `editor.tabSize` and `editor.insertSpaces` will be detected based on the file contents.")
'description': nls.localize('detectIndentation', "Controls whether `#editor.tabSize#` and `#editor.insertSpaces#` will be automatically detected when a file is opened based on the file contents.")
},
'editor.roundedSelection': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.roundedSelection,
'description': nls.localize('roundedSelection', "Controls if selections have rounded corners")
'description': nls.localize('roundedSelection', "Controls whether selections should have rounded corners.")
},
'editor.scrollBeyondLastLine': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastLine,
'description': nls.localize('scrollBeyondLastLine', "Controls if the editor will scroll beyond the last line")
'description': nls.localize('scrollBeyondLastLine', "Controls whether the editor will scroll beyond the last line.")
},
'editor.scrollBeyondLastColumn': {
'type': 'number',
'default': EDITOR_DEFAULTS.viewInfo.scrollBeyondLastColumn,
'description': nls.localize('scrollBeyondLastColumn', "Controls the number of extra characters beyond which the editor will scroll horizontally.")
},
'editor.smoothScrolling': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.smoothScrolling,
'description': nls.localize('smoothScrolling', "Controls if the editor will scroll using an animation")
'description': nls.localize('smoothScrolling', "Controls whether the editor will scroll using an animation.")
},
'editor.minimap.enabled': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.minimap.enabled,
'description': nls.localize('minimap.enabled', "Controls if the minimap is shown")
'description': nls.localize('minimap.enabled', "Controls whether the minimap is shown.")
},
'editor.minimap.side': {
'type': 'string',
@@ -279,27 +336,42 @@ const editorConfiguration: IConfigurationNode = {
'editor.minimap.renderCharacters': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.minimap.renderCharacters,
'description': nls.localize('minimap.renderCharacters', "Render the actual characters on a line (as opposed to color blocks)")
'description': nls.localize('minimap.renderCharacters', "Render the actual characters on a line as opposed to color blocks.")
},
'editor.minimap.maxColumn': {
'type': 'number',
'default': EDITOR_DEFAULTS.viewInfo.minimap.maxColumn,
'description': nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns")
'description': nls.localize('minimap.maxColumn', "Limit the width of the minimap to render at most a certain number of columns.")
},
'editor.hover.enabled': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.hover.enabled,
'description': nls.localize('hover.enabled', "Controls whether the hover is shown.")
},
'editor.hover.delay': {
'type': 'number',
'default': EDITOR_DEFAULTS.contribInfo.hover.delay,
'description': nls.localize('hover.delay', "Time delay in milliseconds after which to the hover is shown.")
},
'editor.hover.sticky': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.hover.sticky,
'description': nls.localize('hover.sticky', "Controls whether the hover should remain visible when mouse is moved over it.")
},
'editor.find.seedSearchStringFromSelection': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.find.seedSearchStringFromSelection,
'description': nls.localize('find.seedSearchStringFromSelection', "Controls if we seed the search string in Find Widget from editor selection")
'description': nls.localize('find.seedSearchStringFromSelection', "Controls whether the search string in the Find Widget is seeded from the editor selection.")
},
'editor.find.autoFindInSelection': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.find.autoFindInSelection,
'description': nls.localize('find.autoFindInSelection', "Controls if Find in Selection flag is turned on when multiple characters or lines of text are selected in the editor")
'description': nls.localize('find.autoFindInSelection', "Controls whether the find operation is carried on selected text or the entire file in the editor.")
},
'editor.find.globalFindClipboard': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.find.globalFindClipboard,
'description': nls.localize('find.globalFindClipboard', "Controls if the Find Widget should read or modify the shared find clipboard on macOS"),
'description': nls.localize('find.globalFindClipboard', "Controls whether the Find Widget should read or modify the shared find clipboard on macOS."),
'included': platform.isMacintosh
},
'editor.wordWrap': {
@@ -313,14 +385,14 @@ const editorConfiguration: IConfigurationNode = {
comment: [
'- `editor.wordWrapColumn` refers to a different setting and should not be localized.'
]
}, "Lines will wrap at `editor.wordWrapColumn`."),
}, "Lines will wrap at `#editor.wordWrapColumn#`."),
nls.localize({
key: 'wordWrap.bounded',
comment: [
'- viewport means the edge of the visible window size.',
'- `editor.wordWrapColumn` refers to a different setting and should not be localized.'
]
}, "Lines will wrap at the minimum of viewport and `editor.wordWrapColumn`."),
}, "Lines will wrap at the minimum of viewport and `#editor.wordWrapColumn#`."),
],
'default': EDITOR_DEFAULTS.wordWrap,
'description': nls.localize({
@@ -329,7 +401,7 @@ const editorConfiguration: IConfigurationNode = {
'- \'off\', \'on\', \'wordWrapColumn\' and \'bounded\' refer to values the setting can take and should not be localized.',
'- `editor.wordWrapColumn` refers to a different setting and should not be localized.'
]
}, "Controls how lines should wrap. Can be:\n - 'off' (disable wrapping),\n - 'on' (viewport wrapping),\n - 'wordWrapColumn' (wrap at `editor.wordWrapColumn`) or\n - 'bounded' (wrap at minimum of viewport and `editor.wordWrapColumn`).")
}, "Controls how lines should wrap.")
},
'editor.wordWrapColumn': {
'type': 'integer',
@@ -341,18 +413,24 @@ const editorConfiguration: IConfigurationNode = {
'- `editor.wordWrap` refers to a different setting and should not be localized.',
'- \'wordWrapColumn\' and \'bounded\' refer to values the different setting can take and should not be localized.'
]
}, "Controls the wrapping column of the editor when `editor.wordWrap` is 'wordWrapColumn' or 'bounded'.")
}, "Controls the wrapping column of the editor when `#editor.wordWrap#` is `wordWrapColumn` or `bounded`.")
},
'editor.wrappingIndent': {
'type': 'string',
'enum': ['none', 'same', 'indent'],
'enum': ['none', 'same', 'indent', 'deepIndent'],
enumDescriptions: [
nls.localize('wrappingIndent.none', "No indentation. Wrapped lines begin at column 1."),
nls.localize('wrappingIndent.same', "Wrapped lines get the same indentation as the parent."),
nls.localize('wrappingIndent.indent', "Wrapped lines get +1 indentation toward the parent."),
nls.localize('wrappingIndent.deepIndent', "Wrapped lines get +2 indentation toward the parent."),
],
'default': 'same',
'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines. Can be one of 'none', 'same' or 'indent'.")
'description': nls.localize('wrappingIndent', "Controls the indentation of wrapped lines."),
},
'editor.mouseWheelScrollSensitivity': {
'type': 'number',
'default': EDITOR_DEFAULTS.viewInfo.scrollbar.mouseWheelScrollSensitivity,
'description': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events")
'description': nls.localize('mouseWheelScrollSensitivity', "A multiplier to be used on the `deltaX` and `deltaY` of mouse wheel scroll events.")
},
'editor.multiCursorModifier': {
'type': 'string',
@@ -368,7 +446,7 @@ const editorConfiguration: IConfigurationNode = {
'- `ctrlCmd` refers to a value the setting can take and should not be localized.',
'- `Control` and `Command` refer to the modifier keys Ctrl or Cmd on the keyboard and can be localized.'
]
}, "The modifier to be used to add multiple cursors with the mouse. `ctrlCmd` maps to `Control` on Windows and Linux and to `Command` on macOS. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier.")
}, "The modifier to be used to add multiple cursors with the mouse. The Go To Definition and Open Link mouse gestures will adapt such that they do not conflict with the multicursor modifier. [Read more](https://code.visualstudio.com/docs/editor/codebasics#_multicursor-modifier).")
},
'editor.multiCursorMergeOverlapping': {
'type': 'boolean',
@@ -402,54 +480,59 @@ const editorConfiguration: IConfigurationNode = {
}
],
'default': EDITOR_DEFAULTS.contribInfo.quickSuggestions,
'description': nls.localize('quickSuggestions', "Controls if suggestions should automatically show up while typing")
'description': nls.localize('quickSuggestions', "Controls whether suggestions should automatically show up while typing.")
},
'editor.quickSuggestionsDelay': {
'type': 'integer',
'default': EDITOR_DEFAULTS.contribInfo.quickSuggestionsDelay,
'minimum': 0,
'description': nls.localize('quickSuggestionsDelay', "Controls the delay in ms after which quick suggestions will show up")
'description': nls.localize('quickSuggestionsDelay', "Controls the delay in milliseconds after which quick suggestions will show up.")
},
'editor.parameterHints': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.parameterHints,
'description': nls.localize('parameterHints', "Enables pop-up that shows parameter documentation and type information as you type")
'description': nls.localize('parameterHints', "Enables a pop-up that shows parameter documentation and type information as you type.")
},
'editor.autoClosingBrackets': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.autoClosingBrackets,
'description': nls.localize('autoClosingBrackets', "Controls if the editor should automatically close brackets after opening them")
'description': nls.localize('autoClosingBrackets', "Controls whether the editor should automatically close brackets after the user adds an opening bracket.")
},
'editor.formatOnType': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.formatOnType,
'description': nls.localize('formatOnType', "Controls if the editor should automatically format the line after typing")
'description': nls.localize('formatOnType', "Controls whether the editor should automatically format the line after typing.")
},
'editor.formatOnPaste': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.formatOnPaste,
'description': nls.localize('formatOnPaste', "Controls if the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.")
'description': nls.localize('formatOnPaste', "Controls whether the editor should automatically format the pasted content. A formatter must be available and the formatter should be able to format a range in a document.")
},
'editor.autoIndent': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.autoIndent,
'description': nls.localize('autoIndent', "Controls if the editor should automatically adjust the indentation when users type, paste or move lines. Indentation rules of the language must be available.")
'description': nls.localize('autoIndent', "Controls whether the editor should automatically adjust the indentation when users type, paste or move lines. Extensions with indentation rules of the language must be available.")
},
'editor.suggestOnTriggerCharacters': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.suggestOnTriggerCharacters,
'description': nls.localize('suggestOnTriggerCharacters', "Controls if suggestions should automatically show up when typing trigger characters")
'description': nls.localize('suggestOnTriggerCharacters', "Controls whether suggestions should automatically show up when typing trigger characters.")
},
'editor.acceptSuggestionOnEnter': {
'type': 'string',
'enum': ['on', 'smart', 'off'],
'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnEnter,
'description': nls.localize('acceptSuggestionOnEnter', "Controls if suggestions should be accepted on 'Enter' - in addition to 'Tab'. Helps to avoid ambiguity between inserting new lines or accepting suggestions. The value 'smart' means only accept a suggestion with Enter when it makes a textual change")
'enumDescriptions': [
'',
nls.localize('acceptSuggestionOnEnterSmart', "Only accept a suggestion with `Enter` when it makes a textual change."),
''
],
'description': nls.localize('acceptSuggestionOnEnter', "Controls whether suggestions should be accepted on `Enter`, in addition to `Tab`. Helps to avoid ambiguity between inserting new lines or accepting suggestions.")
},
'editor.acceptSuggestionOnCommitCharacter': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.acceptSuggestionOnCommitCharacter,
'description': nls.localize('acceptSuggestionOnCommitCharacter', "Controls if suggestions should be accepted on commit characters. For instance in JavaScript the semi-colon (';') can be a commit character that accepts a suggestion and types that character.")
'description': nls.localize('acceptSuggestionOnCommitCharacter', "Controls whether suggestions should be accepted on commit characters. For example, in JavaScript, the semi-colon (`;`) can be a commit character that accepts a suggestion and types that character.")
},
'editor.snippetSuggestions': {
'type': 'string',
@@ -460,7 +543,7 @@ const editorConfiguration: IConfigurationNode = {
nls.localize('snippetSuggestions.inline', "Show snippets suggestions with other suggestions."),
nls.localize('snippetSuggestions.none', "Do not show snippet suggestions."),
],
'default': EDITOR_DEFAULTS.contribInfo.snippetSuggestions,
'default': EDITOR_DEFAULTS.contribInfo.suggest.snippets,
'description': nls.localize('snippetSuggestions', "Controls whether snippets are shown with other suggestions and how they are sorted.")
},
'editor.emptySelectionClipboard': {
@@ -488,33 +571,43 @@ const editorConfiguration: IConfigurationNode = {
'type': 'integer',
'default': 0,
'minimum': 0,
'description': nls.localize('suggestFontSize', "Font size for the suggest widget")
'description': nls.localize('suggestFontSize', "Font size for the suggest widget.")
},
'editor.suggestLineHeight': {
'type': 'integer',
'default': 0,
'minimum': 0,
'description': nls.localize('suggestLineHeight', "Line height for the suggest widget")
'description': nls.localize('suggestLineHeight', "Line height for the suggest widget.")
},
'editor.suggest.filterGraceful': {
type: 'boolean',
default: true,
description: nls.localize('suggest.filterGraceful', "Controls whether filtering and sorting suggestions accounts for small typos.")
},
'editor.suggest.snippetsPreventQuickSuggestions': {
type: 'boolean',
default: true,
description: nls.localize('suggest.snippetsPreventQuickSuggestions', "Control whether an active snippet prevents quick suggestions.")
},
'editor.selectionHighlight': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.selectionHighlight,
'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight similar matches to the selection")
'description': nls.localize('selectionHighlight', "Controls whether the editor should highlight matches similar to the selection")
},
'editor.occurrencesHighlight': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.occurrencesHighlight,
'description': nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences")
'description': nls.localize('occurrencesHighlight', "Controls whether the editor should highlight semantic symbol occurrences.")
},
'editor.overviewRulerLanes': {
'type': 'integer',
'default': 3,
'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler")
'description': nls.localize('overviewRulerLanes', "Controls the number of decorations that can show up at the same position in the overview ruler.")
},
'editor.overviewRulerBorder': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.overviewRulerBorder,
'description': nls.localize('overviewRulerBorder', "Controls if a border should be drawn around the overview ruler.")
'description': nls.localize('overviewRulerBorder', "Controls whether a border should be drawn around the overview ruler.")
},
'editor.cursorBlinking': {
'type': 'string',
@@ -525,55 +618,71 @@ const editorConfiguration: IConfigurationNode = {
'editor.mouseWheelZoom': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.mouseWheelZoom,
'description': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding Ctrl")
'description': nls.localize('mouseWheelZoom', "Zoom the font of the editor when using mouse wheel and holding `Ctrl`.")
},
'editor.cursorStyle': {
'type': 'string',
'enum': ['block', 'block-outline', 'line', 'line-thin', 'underline', 'underline-thin'],
'default': editorOptions.cursorStyleToString(EDITOR_DEFAULTS.viewInfo.cursorStyle),
'description': nls.localize('cursorStyle', "Controls the cursor style, accepted values are 'block', 'block-outline', 'line', 'line-thin', 'underline' and 'underline-thin'")
'description': nls.localize('cursorStyle', "Controls the cursor style.")
},
'editor.cursorWidth': {
'type': 'integer',
'default': EDITOR_DEFAULTS.viewInfo.cursorWidth,
'description': nls.localize('cursorWidth', "Controls the width of the cursor when editor.cursorStyle is set to 'line'")
'description': nls.localize('cursorWidth', "Controls the width of the cursor when `#editor.cursorStyle#` is set to `line`.")
},
'editor.fontLigatures': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.fontLigatures,
'description': nls.localize('fontLigatures', "Enables font ligatures")
'description': nls.localize('fontLigatures', "Enables/Disables font ligatures.")
},
'editor.hideCursorInOverviewRuler': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.viewInfo.hideCursorInOverviewRuler,
'description': nls.localize('hideCursorInOverviewRuler', "Controls if the cursor should be hidden in the overview ruler.")
'description': nls.localize('hideCursorInOverviewRuler', "Controls whether the cursor should be hidden in the overview ruler.")
},
'editor.renderWhitespace': {
'type': 'string',
'enum': ['none', 'boundary', 'all'],
'enumDescriptions': [
'',
nls.localize('renderWhiteSpace.boundary', "Render whitespace characters except for single spaces between words."),
''
],
default: EDITOR_DEFAULTS.viewInfo.renderWhitespace,
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters, possibilities are 'none', 'boundary', and 'all'. The 'boundary' option does not render single spaces between words.")
description: nls.localize('renderWhitespace', "Controls how the editor should render whitespace characters.")
},
'editor.renderControlCharacters': {
'type': 'boolean',
default: EDITOR_DEFAULTS.viewInfo.renderControlCharacters,
description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters")
description: nls.localize('renderControlCharacters', "Controls whether the editor should render control characters.")
},
'editor.renderIndentGuides': {
'type': 'boolean',
default: EDITOR_DEFAULTS.viewInfo.renderIndentGuides,
description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides")
description: nls.localize('renderIndentGuides', "Controls whether the editor should render indent guides.")
},
'editor.highlightActiveIndentGuide': {
'type': 'boolean',
default: EDITOR_DEFAULTS.viewInfo.highlightActiveIndentGuide,
description: nls.localize('highlightActiveIndentGuide', "Controls whether the editor should highlight the active indent guide.")
},
'editor.renderLineHighlight': {
'type': 'string',
'enum': ['none', 'gutter', 'line', 'all'],
'enumDescriptions': [
'',
'',
'',
nls.localize('renderLineHighlight.all', "Highlights both the gutter and the current line."),
],
default: EDITOR_DEFAULTS.viewInfo.renderLineHighlight,
description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight, possibilities are 'none', 'gutter', 'line', and 'all'.")
description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight.")
},
'editor.codeLens': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.codeLens,
'description': nls.localize('codeLens', "Controls if the editor shows CodeLens")
'description': nls.localize('codeLens', "Controls whether the editor shows CodeLens")
},
'editor.folding': {
'type': 'boolean',
@@ -583,12 +692,8 @@ const editorConfiguration: IConfigurationNode = {
'editor.foldingStrategy': {
'type': 'string',
'enum': ['auto', 'indentation'],
'enumDescriptions': [
nls.localize('foldingStrategyAuto', 'If available, use a language specific folding strategy, otherwise falls back to the indentation based strategy.'),
nls.localize('foldingStrategyIndentation', 'Always use the indentation based folding strategy')
],
'default': EDITOR_DEFAULTS.contribInfo.foldingStrategy,
'description': nls.localize('foldingStrategy', "Controls the way folding ranges are computed. 'auto' picks uses a language specific folding strategy, if available. 'indentation' forces that the indentation based folding strategy is used.")
'description': nls.localize('foldingStrategy', "Controls the strategy for computing folding ranges. `auto` uses a language specific folding strategy, if available. `indentation` uses the indentation based folding strategy.")
},
'editor.showFoldingControls': {
'type': 'string',
@@ -609,22 +714,22 @@ const editorConfiguration: IConfigurationNode = {
'editor.useTabStops': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.useTabStops,
'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops")
'description': nls.localize('useTabStops', "Inserting and deleting whitespace follows tab stops.")
},
'editor.trimAutoWhitespace': {
'type': 'boolean',
'default': EDITOR_MODEL_DEFAULTS.trimAutoWhitespace,
'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace")
'description': nls.localize('trimAutoWhitespace', "Remove trailing auto inserted whitespace.")
},
'editor.stablePeek': {
'type': 'boolean',
'default': false,
'description': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting Escape.")
'description': nls.localize('stablePeek', "Keep peek editors open even when double clicking their content or when hitting `Escape`.")
},
'editor.dragAndDrop': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.dragAndDrop,
'description': nls.localize('dragAndDrop', "Controls if the editor should allow to move selections via drag and drop.")
'description': nls.localize('dragAndDrop', "Controls whether the editor should allow moving selections via drag and drop.")
},
'editor.accessibilitySupport': {
'type': 'string',
@@ -637,10 +742,15 @@ const editorConfiguration: IConfigurationNode = {
'default': EDITOR_DEFAULTS.accessibilitySupport,
'description': nls.localize('accessibilitySupport', "Controls whether the editor should run in a mode where it is optimized for screen readers.")
},
'editor.showUnused': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.showUnused,
'description': nls.localize('showUnused', "Controls fading out of unused code.")
},
'editor.links': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.links,
'description': nls.localize('links', "Controls whether the editor should detect links and make them clickable")
'description': nls.localize('links', "Controls whether the editor should detect links and make them clickable.")
},
'editor.colorDecorators': {
'type': 'boolean',
@@ -650,14 +760,14 @@ const editorConfiguration: IConfigurationNode = {
'editor.lightbulb.enabled': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.lightbulbEnabled,
'description': nls.localize('codeActions', "Enables the code action lightbulb")
'description': nls.localize('codeActions', "Enables the code action lightbulb in the editor.")
},
'editor.codeActionsOnSave': {
'type': 'object',
'properties': {
'source.organizeImports': {
'type': 'boolean',
'description': nls.localize('codeActionsOnSave.organizeImports', "Run organize imports on save?")
'description': nls.localize('codeActionsOnSave.organizeImports', "Controls whether organize imports action should be run on file save.")
}
},
'additionalProperties': {
@@ -669,23 +779,23 @@ const editorConfiguration: IConfigurationNode = {
'editor.codeActionsOnSaveTimeout': {
'type': 'number',
'default': EDITOR_DEFAULTS.contribInfo.codeActionsOnSaveTimeout,
'description': nls.localize('codeActionsOnSaveTimeout', "Timeout for code actions run on save.")
'description': nls.localize('codeActionsOnSaveTimeout', "Timeout in milliseconds after which the code actions that are run on save are cancelled.")
},
'editor.selectionClipboard': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.contribInfo.selectionClipboard,
'description': nls.localize('selectionClipboard', "Controls if the Linux primary clipboard should be supported."),
'description': nls.localize('selectionClipboard', "Controls whether the Linux primary clipboard should be supported."),
'included': platform.isLinux
},
'diffEditor.renderSideBySide': {
'type': 'boolean',
'default': true,
'description': nls.localize('sideBySide', "Controls if the diff editor shows the diff side by side or inline")
'description': nls.localize('sideBySide', "Controls whether the diff editor shows the diff side by side or inline.")
},
'diffEditor.ignoreTrimWhitespace': {
'type': 'boolean',
'default': true,
'description': nls.localize('ignoreTrimWhitespace', "Controls if the diff editor shows changes in leading or trailing whitespace as diffs")
'description': nls.localize('ignoreTrimWhitespace', "Controls whether the diff editor shows changes in leading or trailing whitespace as diffs.")
},
'editor.largeFileOptimizations': {
'type': 'boolean',
@@ -695,9 +805,29 @@ const editorConfiguration: IConfigurationNode = {
'diffEditor.renderIndicators': {
'type': 'boolean',
'default': true,
'description': nls.localize('renderIndicators', "Controls if the diff editor shows +/- indicators for added/removed changes")
'description': nls.localize('renderIndicators', "Controls whether the diff editor shows +/- indicators for added/removed changes.")
}
}
};
let cachedEditorConfigurationKeys: { [key: string]: boolean; } = null;
function getEditorConfigurationKeys(): { [key: string]: boolean; } {
if (cachedEditorConfigurationKeys === null) {
cachedEditorConfigurationKeys = Object.create(null);
Object.keys(editorConfiguration.properties).forEach((prop) => {
cachedEditorConfigurationKeys[prop] = true;
});
}
return cachedEditorConfigurationKeys;
}
export function isEditorConfigurationKey(key: string): boolean {
const editorConfigurationKeys = getEditorConfigurationKeys();
return (editorConfigurationKeys[`editor.${key}`] || false);
}
export function isDiffEditorConfigurationKey(key: string): boolean {
const editorConfigurationKeys = getEditorConfigurationKeys();
return (editorConfigurationKeys[`diffEditor.${key}`] || false);
}
configurationRegistry.registerConfiguration(editorConfiguration);

View File

@@ -137,6 +137,38 @@ export interface IEditorLightbulbOptions {
enabled?: boolean;
}
/**
* Configuration options for editor hover
*/
export interface IEditorHoverOptions {
/**
* Enable the hover.
* Defaults to true.
*/
enabled?: boolean;
/**
* Delay for showing the hover.
* Defaults to 300.
*/
delay?: number;
/**
* Is the hover sticky such that it can be clicked and its contents selected?
* Defaults to true.
*/
sticky?: boolean;
}
export interface ISuggestOptions {
/**
* Enable graceful matching. Defaults to true.
*/
filterGraceful?: boolean;
/**
* Prevent quick suggestions when a snippet is active. Defaults to true.
*/
snippetsPreventQuickSuggestions?: boolean;
}
/**
* Configuration map for codeActionsOnSave
*/
@@ -300,6 +332,11 @@ export interface IEditorOptions {
* Defaults to true.
*/
scrollBeyondLastLine?: boolean;
/**
* Enable that scrolling can go beyond the last column by a number of columns.
* Defaults to 5.
*/
scrollBeyondLastColumn?: number;
/**
* Enable that the editor animates scrolling to a position.
* Defaults to false.
@@ -335,7 +372,7 @@ export interface IEditorOptions {
*/
wordWrapMinified?: boolean;
/**
* Control indentation of wrapped lines. Can be: 'none', 'same' or 'indent'.
* Control indentation of wrapped lines. Can be: 'none', 'same', 'indent' or 'deepIndent'.
* Defaults to 'same' in vscode and to 'none' in monaco-editor.
*/
wrappingIndent?: string;
@@ -362,10 +399,9 @@ export interface IEditorOptions {
*/
stopRenderingLineAfter?: number;
/**
* Enable hover.
* Defaults to true.
* Configure the editor's hover.
*/
hover?: boolean;
hover?: IEditorHoverOptions;
/**
* Enable detecting links and making them clickable.
* Defaults to true.
@@ -400,6 +436,10 @@ export interface IEditorOptions {
* Defaults to 'auto'. It is best to leave this to 'auto'.
*/
accessibilitySupport?: 'auto' | 'off' | 'on';
/**
* Suggest options.
*/
suggest?: ISuggestOptions;
/**
* Enable quick suggestions (shadow suggestions)
* Defaults to true.
@@ -544,9 +584,14 @@ export interface IEditorOptions {
renderControlCharacters?: boolean;
/**
* Enable rendering of indent guides.
* Defaults to false.
* Defaults to true.
*/
renderIndentGuides?: boolean;
/**
* Enable highlighting of the active indent guide.
* Defaults to true.
*/
highlightActiveIndentGuide?: boolean;
/**
* Enable rendering of current line highlight.
* Defaults to all.
@@ -576,6 +621,10 @@ export interface IEditorOptions {
* The letter spacing
*/
letterSpacing?: number;
/**
* Controls fading out of unused variables.
*/
showUnused?: boolean;
}
/**
@@ -630,9 +679,13 @@ export enum WrappingIndent {
*/
Same = 1,
/**
* Indent => wrapped lines get +1 indentation as the parent.
* Indent => wrapped lines get +1 indentation toward the parent.
*/
Indent = 2
Indent = 2,
/**
* DeepIndent => wrapped lines get +2 indentation toward the parent.
*/
DeepIndent = 3
}
/**
@@ -786,6 +839,18 @@ export interface InternalEditorFindOptions {
readonly globalFindClipboard: boolean;
}
export interface InternalEditorHoverOptions {
readonly enabled: boolean;
readonly delay: number;
readonly sticky: boolean;
}
export interface InternalSuggestOptions {
readonly filterGraceful: boolean;
readonly snippets: 'top' | 'bottom' | 'inline' | 'none';
readonly snippetsPreventQuickSuggestions: boolean;
}
export interface EditorWrappingInfo {
readonly inDiffEditor: boolean;
readonly isDominatedByLongLines: boolean;
@@ -825,12 +890,14 @@ export interface InternalEditorViewOptions {
readonly cursorWidth: number;
readonly hideCursorInOverviewRuler: boolean;
readonly scrollBeyondLastLine: boolean;
readonly scrollBeyondLastColumn: number;
readonly smoothScrolling: boolean;
readonly stopRenderingLineAfter: number;
readonly renderWhitespace: 'none' | 'boundary' | 'all';
readonly renderControlCharacters: boolean;
readonly fontLigatures: boolean;
readonly renderIndentGuides: boolean;
readonly highlightActiveIndentGuide: boolean;
readonly renderLineHighlight: 'none' | 'gutter' | 'line' | 'all';
readonly scrollbar: InternalEditorScrollbarOptions;
readonly minimap: InternalEditorMinimapOptions;
@@ -839,7 +906,7 @@ export interface InternalEditorViewOptions {
export interface EditorContribOptions {
readonly selectionClipboard: boolean;
readonly hover: boolean;
readonly hover: InternalEditorHoverOptions;
readonly links: boolean;
readonly contextmenu: boolean;
readonly quickSuggestions: boolean | { other: boolean, comments: boolean, strings: boolean };
@@ -851,11 +918,12 @@ export interface EditorContribOptions {
readonly suggestOnTriggerCharacters: boolean;
readonly acceptSuggestionOnEnter: 'on' | 'smart' | 'off';
readonly acceptSuggestionOnCommitCharacter: boolean;
readonly snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none';
// readonly snippetSuggestions: 'top' | 'bottom' | 'inline' | 'none';
readonly wordBasedSuggestions: boolean;
readonly suggestSelection: 'first' | 'recentlyUsed' | 'recentlyUsedByPrefix';
readonly suggestFontSize: number;
readonly suggestLineHeight: number;
readonly suggest: InternalSuggestOptions;
readonly selectionHighlight: boolean;
readonly occurrencesHighlight: boolean;
readonly codeLens: boolean;
@@ -899,6 +967,7 @@ export interface IValidatedEditorOptions {
readonly multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey';
readonly multiCursorMergeOverlapping: boolean;
readonly accessibilitySupport: 'auto' | 'off' | 'on';
readonly showUnused: boolean;
readonly viewInfo: InternalEditorViewOptions;
readonly contribInfo: EditorContribOptions;
@@ -921,6 +990,7 @@ export class InternalEditorOptions {
readonly accessibilitySupport: platform.AccessibilitySupport;
readonly multiCursorModifier: 'altKey' | 'ctrlKey' | 'metaKey';
readonly multiCursorMergeOverlapping: boolean;
readonly showUnused: boolean;
// ---- cursor options
readonly wordSeparators: string;
@@ -962,6 +1032,7 @@ export class InternalEditorOptions {
viewInfo: InternalEditorViewOptions;
wrappingInfo: EditorWrappingInfo;
contribInfo: EditorContribOptions;
showUnused: boolean;
}) {
this.canUseLayerHinting = source.canUseLayerHinting;
this.pixelRatio = source.pixelRatio;
@@ -983,6 +1054,7 @@ export class InternalEditorOptions {
this.viewInfo = source.viewInfo;
this.wrappingInfo = source.wrappingInfo;
this.contribInfo = source.contribInfo;
this.showUnused = source.showUnused;
}
/**
@@ -1004,6 +1076,7 @@ export class InternalEditorOptions {
&& this.useTabStops === other.useTabStops
&& this.tabFocusMode === other.tabFocusMode
&& this.dragAndDrop === other.dragAndDrop
&& this.showUnused === other.showUnused
&& this.emptySelectionClipboard === other.emptySelectionClipboard
&& InternalEditorOptions._equalsLayoutInfo(this.layoutInfo, other.layoutInfo)
&& this.fontInfo.equals(other.fontInfo)
@@ -1105,12 +1178,14 @@ export class InternalEditorOptions {
&& a.cursorWidth === b.cursorWidth
&& a.hideCursorInOverviewRuler === b.hideCursorInOverviewRuler
&& a.scrollBeyondLastLine === b.scrollBeyondLastLine
&& a.scrollBeyondLastColumn === b.scrollBeyondLastColumn
&& a.smoothScrolling === b.smoothScrolling
&& a.stopRenderingLineAfter === b.stopRenderingLineAfter
&& a.renderWhitespace === b.renderWhitespace
&& a.renderControlCharacters === b.renderControlCharacters
&& a.fontLigatures === b.fontLigatures
&& a.renderIndentGuides === b.renderIndentGuides
&& a.highlightActiveIndentGuide === b.highlightActiveIndentGuide
&& a.renderLineHighlight === b.renderLineHighlight
&& this._equalsScrollbarOptions(a.scrollbar, b.scrollbar)
&& this._equalsMinimapOptions(a.minimap, b.minimap)
@@ -1154,7 +1229,6 @@ export class InternalEditorOptions {
/**
* @internal
*/
private static _equalFindOptions(a: InternalEditorFindOptions, b: InternalEditorFindOptions): boolean {
return (
a.seedSearchStringFromSelection === b.seedSearchStringFromSelection
@@ -1163,6 +1237,32 @@ export class InternalEditorOptions {
);
}
/**
* @internal
*/
private static _equalsHoverOptions(a: InternalEditorHoverOptions, b: InternalEditorHoverOptions): boolean {
return (
a.enabled === b.enabled
&& a.delay === b.delay
&& a.sticky === b.sticky
);
}
/**
* @internal
*/
private static _equalsSuggestOptions(a: InternalSuggestOptions, b: InternalSuggestOptions): any {
if (a === b) {
return true;
} else if (!a || !b) {
return false;
} else {
return a.filterGraceful === b.filterGraceful
&& a.snippets === b.snippets
&& a.snippetsPreventQuickSuggestions === b.snippetsPreventQuickSuggestions;
}
}
/**
* @internal
*/
@@ -1186,7 +1286,7 @@ export class InternalEditorOptions {
private static _equalsContribOptions(a: EditorContribOptions, b: EditorContribOptions): boolean {
return (
a.selectionClipboard === b.selectionClipboard
&& a.hover === b.hover
&& this._equalsHoverOptions(a.hover, b.hover)
&& a.links === b.links
&& a.contextmenu === b.contextmenu
&& InternalEditorOptions._equalsQuickSuggestions(a.quickSuggestions, b.quickSuggestions)
@@ -1198,11 +1298,11 @@ export class InternalEditorOptions {
&& a.suggestOnTriggerCharacters === b.suggestOnTriggerCharacters
&& a.acceptSuggestionOnEnter === b.acceptSuggestionOnEnter
&& a.acceptSuggestionOnCommitCharacter === b.acceptSuggestionOnCommitCharacter
&& a.snippetSuggestions === b.snippetSuggestions
&& a.wordBasedSuggestions === b.wordBasedSuggestions
&& a.suggestSelection === b.suggestSelection
&& a.suggestFontSize === b.suggestFontSize
&& a.suggestLineHeight === b.suggestLineHeight
&& this._equalsSuggestOptions(a.suggest, b.suggest)
&& a.selectionHighlight === b.selectionHighlight
&& a.occurrencesHighlight === b.occurrencesHighlight
&& a.codeLens === b.codeLens
@@ -1470,10 +1570,12 @@ function _wrappingIndentFromString(wrappingIndent: string, defaultValue: Wrappin
if (typeof wrappingIndent !== 'string') {
return defaultValue;
}
if (wrappingIndent === 'indent') {
return WrappingIndent.Indent;
} else if (wrappingIndent === 'same') {
if (wrappingIndent === 'same') {
return WrappingIndent.Same;
} else if (wrappingIndent === 'indent') {
return WrappingIndent.Indent;
} else if (wrappingIndent === 'deepIndent') {
return WrappingIndent.DeepIndent;
} else {
return WrappingIndent.None;
}
@@ -1572,6 +1674,7 @@ export class EditorOptionsValidator {
multiCursorModifier: multiCursorModifier,
multiCursorMergeOverlapping: _boolean(opts.multiCursorMergeOverlapping, defaults.multiCursorMergeOverlapping),
accessibilitySupport: _stringSet<'auto' | 'on' | 'off'>(opts.accessibilitySupport, defaults.accessibilitySupport, ['auto', 'on', 'off']),
showUnused: _boolean(opts.showUnused, defaults.showUnused),
viewInfo: viewInfo,
contribInfo: contribInfo,
};
@@ -1629,6 +1732,35 @@ export class EditorOptionsValidator {
};
}
private static _santizeHoverOpts(_opts: boolean | IEditorHoverOptions, defaults: InternalEditorHoverOptions): InternalEditorHoverOptions {
let opts: IEditorHoverOptions;
if (typeof _opts === 'boolean') {
opts = {
enabled: _opts
};
} else if (typeof _opts === 'object') {
opts = _opts;
} else {
return defaults;
}
return {
enabled: _boolean(opts.enabled, defaults.enabled),
delay: _clampedInt(opts.delay, defaults.delay, 0, 10000),
sticky: _boolean(opts.sticky, defaults.sticky)
};
}
private static _sanitizeSuggestOpts(opts: IEditorOptions, defaults: InternalSuggestOptions): InternalSuggestOptions {
const suggestOpts = opts.suggest || {};
return {
filterGraceful: _boolean(suggestOpts.filterGraceful, defaults.filterGraceful),
snippets: _stringSet<'top' | 'bottom' | 'inline' | 'none'>(opts.snippetSuggestions, defaults.snippets, ['top', 'bottom', 'inline', 'none']),
snippetsPreventQuickSuggestions: _boolean(suggestOpts.snippetsPreventQuickSuggestions, defaults.filterGraceful),
};
}
private static _sanitizeViewInfo(opts: IEditorOptions, defaults: InternalEditorViewOptions): InternalEditorViewOptions {
let rulers: number[] = [];
@@ -1718,12 +1850,14 @@ export class EditorOptionsValidator {
cursorWidth: _clampedInt(opts.cursorWidth, defaults.cursorWidth, 0, Number.MAX_VALUE),
hideCursorInOverviewRuler: _boolean(opts.hideCursorInOverviewRuler, defaults.hideCursorInOverviewRuler),
scrollBeyondLastLine: _boolean(opts.scrollBeyondLastLine, defaults.scrollBeyondLastLine),
scrollBeyondLastColumn: _clampedInt(opts.scrollBeyondLastColumn, defaults.scrollBeyondLastColumn, 0, Constants.MAX_SAFE_SMALL_INTEGER),
smoothScrolling: _boolean(opts.smoothScrolling, defaults.smoothScrolling),
stopRenderingLineAfter: _clampedInt(opts.stopRenderingLineAfter, defaults.stopRenderingLineAfter, -1, Constants.MAX_SAFE_SMALL_INTEGER),
renderWhitespace: renderWhitespace,
renderControlCharacters: _boolean(opts.renderControlCharacters, defaults.renderControlCharacters),
fontLigatures: fontLigatures,
renderIndentGuides: _boolean(opts.renderIndentGuides, defaults.renderIndentGuides),
highlightActiveIndentGuide: _boolean(opts.highlightActiveIndentGuide, defaults.highlightActiveIndentGuide),
renderLineHighlight: renderLineHighlight,
scrollbar: scrollbar,
minimap: minimap,
@@ -1745,7 +1879,7 @@ export class EditorOptionsValidator {
const find = this._santizeFindOpts(opts.find, defaults.find);
return {
selectionClipboard: _boolean(opts.selectionClipboard, defaults.selectionClipboard),
hover: _boolean(opts.hover, defaults.hover),
hover: this._santizeHoverOpts(opts.hover, defaults.hover),
links: _boolean(opts.links, defaults.links),
contextmenu: _boolean(opts.contextmenu, defaults.contextmenu),
quickSuggestions: quickSuggestions,
@@ -1757,11 +1891,11 @@ export class EditorOptionsValidator {
suggestOnTriggerCharacters: _boolean(opts.suggestOnTriggerCharacters, defaults.suggestOnTriggerCharacters),
acceptSuggestionOnEnter: _stringSet<'on' | 'smart' | 'off'>(opts.acceptSuggestionOnEnter, defaults.acceptSuggestionOnEnter, ['on', 'smart', 'off']),
acceptSuggestionOnCommitCharacter: _boolean(opts.acceptSuggestionOnCommitCharacter, defaults.acceptSuggestionOnCommitCharacter),
snippetSuggestions: _stringSet<'top' | 'bottom' | 'inline' | 'none'>(opts.snippetSuggestions, defaults.snippetSuggestions, ['top', 'bottom', 'inline', 'none']),
wordBasedSuggestions: _boolean(opts.wordBasedSuggestions, defaults.wordBasedSuggestions),
suggestSelection: _stringSet<'first' | 'recentlyUsed' | 'recentlyUsedByPrefix'>(opts.suggestSelection, defaults.suggestSelection, ['first', 'recentlyUsed', 'recentlyUsedByPrefix']),
suggestFontSize: _clampedInt(opts.suggestFontSize, defaults.suggestFontSize, 0, 1000),
suggestLineHeight: _clampedInt(opts.suggestLineHeight, defaults.suggestLineHeight, 0, 1000),
suggest: this._sanitizeSuggestOpts(opts, defaults.suggest),
selectionHighlight: _boolean(opts.selectionHighlight, defaults.selectionHighlight),
occurrencesHighlight: _boolean(opts.occurrencesHighlight, defaults.occurrencesHighlight),
codeLens: _boolean(opts.codeLens, defaults.codeLens),
@@ -1810,6 +1944,7 @@ export class InternalEditorOptionsFactory {
multiCursorModifier: opts.multiCursorModifier,
multiCursorMergeOverlapping: opts.multiCursorMergeOverlapping,
accessibilitySupport: opts.accessibilitySupport,
showUnused: opts.showUnused,
viewInfo: {
extraEditorClassName: opts.viewInfo.extraEditorClassName,
@@ -1830,12 +1965,14 @@ export class InternalEditorOptionsFactory {
cursorWidth: opts.viewInfo.cursorWidth,
hideCursorInOverviewRuler: opts.viewInfo.hideCursorInOverviewRuler,
scrollBeyondLastLine: opts.viewInfo.scrollBeyondLastLine,
scrollBeyondLastColumn: opts.viewInfo.scrollBeyondLastColumn,
smoothScrolling: opts.viewInfo.smoothScrolling,
stopRenderingLineAfter: opts.viewInfo.stopRenderingLineAfter,
renderWhitespace: (accessibilityIsOn ? 'none' : opts.viewInfo.renderWhitespace), // DISABLED WHEN SCREEN READER IS ATTACHED
renderControlCharacters: (accessibilityIsOn ? false : opts.viewInfo.renderControlCharacters), // DISABLED WHEN SCREEN READER IS ATTACHED
fontLigatures: (accessibilityIsOn ? false : opts.viewInfo.fontLigatures), // DISABLED WHEN SCREEN READER IS ATTACHED
renderIndentGuides: (accessibilityIsOn ? false : opts.viewInfo.renderIndentGuides), // DISABLED WHEN SCREEN READER IS ATTACHED
highlightActiveIndentGuide: opts.viewInfo.highlightActiveIndentGuide,
renderLineHighlight: opts.viewInfo.renderLineHighlight,
scrollbar: opts.viewInfo.scrollbar,
minimap: {
@@ -1862,11 +1999,11 @@ export class InternalEditorOptionsFactory {
suggestOnTriggerCharacters: opts.contribInfo.suggestOnTriggerCharacters,
acceptSuggestionOnEnter: opts.contribInfo.acceptSuggestionOnEnter,
acceptSuggestionOnCommitCharacter: opts.contribInfo.acceptSuggestionOnCommitCharacter,
snippetSuggestions: opts.contribInfo.snippetSuggestions,
wordBasedSuggestions: opts.contribInfo.wordBasedSuggestions,
suggestSelection: opts.contribInfo.suggestSelection,
suggestFontSize: opts.contribInfo.suggestFontSize,
suggestLineHeight: opts.contribInfo.suggestLineHeight,
suggest: opts.contribInfo.suggest,
selectionHighlight: (accessibilityIsOn ? false : opts.contribInfo.selectionHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED
occurrencesHighlight: (accessibilityIsOn ? false : opts.contribInfo.occurrencesHighlight), // DISABLED WHEN SCREEN READER IS ATTACHED
codeLens: (accessibilityIsOn ? false : opts.contribInfo.codeLens), // DISABLED WHEN SCREEN READER IS ATTACHED
@@ -2030,7 +2167,8 @@ export class InternalEditorOptionsFactory {
fontInfo: env.fontInfo,
viewInfo: opts.viewInfo,
wrappingInfo: wrappingInfo,
contribInfo: opts.contribInfo
contribInfo: opts.contribInfo,
showUnused: opts.showUnused,
});
}
}
@@ -2260,6 +2398,7 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
multiCursorModifier: 'altKey',
multiCursorMergeOverlapping: true,
accessibilitySupport: 'auto',
showUnused: true,
viewInfo: {
extraEditorClassName: '',
@@ -2280,12 +2419,14 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
cursorWidth: 0,
hideCursorInOverviewRuler: false,
scrollBeyondLastLine: true,
scrollBeyondLastColumn: 5,
smoothScrolling: false,
stopRenderingLineAfter: 10000,
renderWhitespace: 'none',
renderControlCharacters: false,
fontLigatures: false,
renderIndentGuides: true,
highlightActiveIndentGuide: true,
renderLineHighlight: 'line',
scrollbar: {
vertical: ScrollbarVisibility.Auto,
@@ -2314,7 +2455,11 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
contribInfo: {
selectionClipboard: true,
hover: true,
hover: {
enabled: true,
delay: 300,
sticky: true
},
links: true,
contextmenu: true,
quickSuggestions: { other: true, comments: false, strings: false },
@@ -2326,11 +2471,15 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
suggestOnTriggerCharacters: true,
acceptSuggestionOnEnter: 'on',
acceptSuggestionOnCommitCharacter: true,
snippetSuggestions: 'inline',
wordBasedSuggestions: true,
suggestSelection: 'recentlyUsed',
suggestFontSize: 0,
suggestLineHeight: 0,
suggest: {
filterGraceful: true,
snippets: 'inline',
snippetsPreventQuickSuggestions: true
},
selectionHighlight: true,
occurrencesHighlight: true,
codeLens: true,

View File

@@ -24,7 +24,7 @@ export const EditorZoom: IEditorZoom = new class implements IEditorZoom {
}
public setZoomLevel(zoomLevel: number): void {
zoomLevel = Math.min(Math.max(-9, zoomLevel), 9);
zoomLevel = Math.min(Math.max(-5, zoomLevel), 20);
if (this._zoomLevel === zoomLevel) {
return;
}

View File

@@ -463,7 +463,6 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
if (handlerId === H.CompositionEnd) {
this._isDoingComposition = false;
return;
}
if (this._configuration.editor.readOnly) {
@@ -523,6 +522,10 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
case H.ExecuteCommands:
this._externalExecuteCommands(<editorCommon.ICommand[]>payload);
break;
case H.CompositionEnd:
this._interpretCompositionEnd(source);
break;
}
} catch (err) {
onUnexpectedError(err);
@@ -539,6 +542,13 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
}
}
private _interpretCompositionEnd(source: string) {
if (!this._isDoingComposition && source === 'keyboard') {
// composition finishes, let's check if we need to auto complete if necessary.
this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.config, this.context.model, this.getSelections()));
}
}
private _type(source: string, text: string): void {
if (!this._isDoingComposition && source === 'keyboard') {
// If this event is coming straight from the keyboard, look for electric characters and enter

View File

@@ -183,41 +183,39 @@ export class CursorCollection {
interface SortedCursor {
index: number;
selection: Selection;
viewSelection: Selection;
}
let sortedCursors: SortedCursor[] = [];
for (let i = 0, len = cursors.length; i < len; i++) {
sortedCursors.push({
index: i,
selection: cursors[i].modelState.selection,
viewSelection: cursors[i].viewState.selection
});
}
sortedCursors.sort((a, b) => {
if (a.viewSelection.startLineNumber === b.viewSelection.startLineNumber) {
return a.viewSelection.startColumn - b.viewSelection.startColumn;
if (a.selection.startLineNumber === b.selection.startLineNumber) {
return a.selection.startColumn - b.selection.startColumn;
}
return a.viewSelection.startLineNumber - b.viewSelection.startLineNumber;
return a.selection.startLineNumber - b.selection.startLineNumber;
});
for (let sortedCursorIndex = 0; sortedCursorIndex < sortedCursors.length - 1; sortedCursorIndex++) {
const current = sortedCursors[sortedCursorIndex];
const next = sortedCursors[sortedCursorIndex + 1];
const currentViewSelection = current.viewSelection;
const nextViewSelection = next.viewSelection;
const currentSelection = current.selection;
const nextSelection = next.selection;
if (!this.context.config.multiCursorMergeOverlapping) {
continue;
}
let shouldMergeCursors: boolean;
if (nextViewSelection.isEmpty() || currentViewSelection.isEmpty()) {
if (nextSelection.isEmpty() || currentSelection.isEmpty()) {
// Merge touching cursors if one of them is collapsed
shouldMergeCursors = nextViewSelection.getStartPosition().isBeforeOrEqual(currentViewSelection.getEndPosition());
shouldMergeCursors = nextSelection.getStartPosition().isBeforeOrEqual(currentSelection.getEndPosition());
} else {
// Merge only overlapping cursors (i.e. allow touching ranges)
shouldMergeCursors = nextViewSelection.getStartPosition().isBefore(currentViewSelection.getEndPosition());
shouldMergeCursors = nextSelection.getStartPosition().isBefore(currentSelection.getEndPosition());
}
if (shouldMergeCursors) {

View File

@@ -115,7 +115,7 @@ export class CursorConfiguration {
this.tabSize = modelOptions.tabSize;
this.insertSpaces = modelOptions.insertSpaces;
this.oneIndent = oneIndent;
this.pageSize = Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2;
this.pageSize = Math.max(1, Math.floor(c.layoutInfo.height / c.fontInfo.lineHeight) - 2);
this.lineHeight = c.lineHeight;
this.useTabStops = c.useTabStops;
this.wordSeparators = c.wordSeparators;

View File

@@ -14,22 +14,30 @@ import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands
export class CursorMoveCommands {
public static addCursorDown(context: CursorContext, cursors: CursorState[]): CursorState[] {
public static addCursorDown(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): CursorState[] {
let result: CursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState));
if (useLogicalLine) {
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateDown(context.config, context.model, cursor.modelState));
} else {
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateDown(context.config, context.viewModel, cursor.viewState));
}
}
return result;
}
public static addCursorUp(context: CursorContext, cursors: CursorState[]): CursorState[] {
public static addCursorUp(context: CursorContext, cursors: CursorState[], useLogicalLine: boolean): CursorState[] {
let result: CursorState[] = [], resultLen = 0;
for (let i = 0, len = cursors.length; i < len; i++) {
const cursor = cursors[i];
result[resultLen++] = new CursorState(cursor.modelState, cursor.viewState);
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState));
if (useLogicalLine) {
result[resultLen++] = CursorState.fromModelState(MoveOperations.translateUp(context.config, context.model, cursor.modelState));
} else {
result[resultLen++] = CursorState.fromViewState(MoveOperations.translateUp(context.config, context.viewModel, cursor.viewState));
}
}
return result;
}
@@ -599,7 +607,7 @@ export namespace CursorMove {
\`\`\`
'left', 'right', 'up', 'down'
'wrappedLineStart', 'wrappedLineEnd', 'wrappedLineColumnCenter'
'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter',
'wrappedLineFirstNonWhitespaceCharacter', 'wrappedLineLastNonWhitespaceCharacter'
'viewPortTop', 'viewPortCenter', 'viewPortBottom', 'viewPortIfOutside'
\`\`\`
* 'by': Unit to move. Default is computed based on 'to' value.

View File

@@ -493,6 +493,23 @@ export class TypeOperations {
});
}
private static _isBeforeClosingBrace(config: CursorConfiguration, ch: string, characterAfter: string) {
const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch);
let isBeforeCloseBrace = false;
for (let otherCloseBrace in config.autoClosingPairsClose) {
const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace);
if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
continue;
}
if (characterAfter === otherCloseBrace) {
isBeforeCloseBrace = true;
break;
}
}
return isBeforeCloseBrace;
}
private static _isAutoClosingOpenCharType(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): boolean {
if (!config.autoClosingBrackets || !config.autoClosingPairsOpen.hasOwnProperty(ch)) {
return false;
@@ -520,19 +537,7 @@ export class TypeOperations {
// Only consider auto closing the pair if a space follows or if another autoclosed pair follows
const characterAfter = lineText.charAt(position.column - 1);
if (characterAfter) {
const thisBraceIsSymmetric = (config.autoClosingPairsOpen[ch] === ch);
let isBeforeCloseBrace = false;
for (let otherCloseBrace in config.autoClosingPairsClose) {
const otherBraceIsSymmetric = (config.autoClosingPairsOpen[otherCloseBrace] === otherCloseBrace);
if (!thisBraceIsSymmetric && otherBraceIsSymmetric) {
continue;
}
if (characterAfter === otherCloseBrace) {
isBeforeCloseBrace = true;
break;
}
}
let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter);
if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) {
return false;
}
@@ -579,6 +584,8 @@ export class TypeOperations {
return false;
}
const isTypingAQuoteCharacter = (ch === '\'' || ch === '"');
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
@@ -603,6 +610,15 @@ export class TypeOperations {
if (selectionContainsOnlyWhitespace) {
return false;
}
if (isTypingAQuoteCharacter && selection.startLineNumber === selection.endLineNumber && selection.startColumn + 1 === selection.endColumn) {
const selectionText = model.getValueInRange(selection);
if ((selectionText === '\'' || selectionText === '"')) {
// Typing a quote character on top of another quote character
// => disable surround selection type
return false;
}
}
}
return true;
@@ -691,6 +707,82 @@ export class TypeOperations {
return null;
}
public static compositionEndWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[]): EditOperationResult {
if (!config.autoClosingBrackets) {
return null;
}
let commands: ICommand[] = [];
for (let i = 0; i < selections.length; i++) {
if (!selections[i].isEmpty()) {
continue;
}
const position = selections[i].getPosition();
const lineText = model.getLineContent(position.lineNumber);
const ch = lineText.charAt(position.column - 2);
if (config.autoClosingPairsClose.hasOwnProperty(ch)) { // first of all, it's a closing tag
if (ch === config.autoClosingPairsClose[ch] /** isEqualPair */) {
const lineTextBeforeCursor = lineText.substr(0, position.column - 2);
const chCntBefore = this._countNeedlesInHaystack(lineTextBeforeCursor, ch);
if (chCntBefore % 2 === 1) {
continue; // it pairs with the opening tag.
}
}
}
// As we are not typing in a new character, so we don't need to run `_runAutoClosingCloseCharType`
// Next step, let's try to check if it's an open char.
if (config.autoClosingPairsOpen.hasOwnProperty(ch)) {
if ((ch === '\'' || ch === '"') && position.column > 2) {
const wordSeparators = getMapForWordSeparators(config.wordSeparators);
const characterBeforeCode = lineText.charCodeAt(position.column - 3);
const characterBeforeType = wordSeparators.get(characterBeforeCode);
if (characterBeforeType === WordCharacterClass.Regular) {
continue;
}
}
const characterAfter = lineText.charAt(position.column - 1);
if (characterAfter) {
let isBeforeCloseBrace = TypeOperations._isBeforeClosingBrace(config, ch, characterAfter);
if (!isBeforeCloseBrace && !/\s/.test(characterAfter)) {
continue;
}
}
if (!model.isCheapToTokenize(position.lineNumber)) {
// Do not force tokenization
continue;
}
model.forceTokenization(position.lineNumber);
const lineTokens = model.getLineTokens(position.lineNumber);
let shouldAutoClosePair = false;
try {
shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column - 1);
} catch (e) {
onUnexpectedError(e);
}
if (shouldAutoClosePair) {
const closeCharacter = config.autoClosingPairsOpen[ch];
commands[i] = new ReplaceCommandWithOffsetCursorState(selections[i], closeCharacter, 0, -closeCharacter.length);
}
}
}
return new EditOperationResult(EditOperationType.Typing, commands, {
shouldPushStackElementBefore: true,
shouldPushStackElementAfter: false
});
}
public static typeWithInterceptors(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string): EditOperationResult {
if (ch === '\n') {

View File

@@ -10,6 +10,7 @@ import { WordCharacterClassifier, WordCharacterClass, getMapForWordSeparators }
import * as strings from 'vs/base/common/strings';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { CharCode } from 'vs/base/common/charCode';
interface IFindWordResult {
/**
@@ -235,7 +236,7 @@ export class WordOperations {
return new Position(lineNumber, column);
}
private static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range {
protected static _deleteWordLeftWhitespace(model: ICursorSimpleModel, position: Position): Range {
const lineContent = model.getLineContent(position.lineNumber);
const startIndex = position.column - 2;
const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex);
@@ -310,7 +311,7 @@ export class WordOperations {
return len;
}
private static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range {
protected static _deleteWordRightWhitespace(model: ICursorSimpleModel, position: Position): Range {
const lineContent = model.getLineContent(position.lineNumber);
const startIndex = position.column - 1;
const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex);
@@ -463,3 +464,143 @@ export class WordOperations {
return cursor.move(true, lineNumber, column, 0);
}
}
export function _lastWordPartEnd(str: string, startIndex: number = str.length - 1): number {
let ignoreUpperCase = !strings.isLowerAsciiLetter(str.charCodeAt(startIndex + 1));
for (let i = startIndex; i >= 0; i--) {
let chCode = str.charCodeAt(i);
if (chCode === CharCode.Space || chCode === CharCode.Tab || (!ignoreUpperCase && strings.isUpperAsciiLetter(chCode)) || chCode === CharCode.Underline) {
return i - 1;
}
if (ignoreUpperCase && i < startIndex && strings.isLowerAsciiLetter(chCode)) {
return i;
}
ignoreUpperCase = ignoreUpperCase && strings.isUpperAsciiLetter(chCode);
}
return -1;
}
export function _nextWordPartBegin(str: string, startIndex: number = 0): number {
let prevChCode = str.charCodeAt(startIndex - 1);
let chCode = str.charCodeAt(startIndex);
// handle the special case ' X' and ' x' which is different from the standard methods
if ((prevChCode === CharCode.Space || prevChCode === CharCode.Tab) && (strings.isLowerAsciiLetter(chCode) || strings.isUpperAsciiLetter(chCode))) {
return startIndex + 1;
}
let ignoreUpperCase = strings.isUpperAsciiLetter(chCode);
for (let i = startIndex; i < str.length; ++i) {
chCode = str.charCodeAt(i);
if (chCode === CharCode.Space || chCode === CharCode.Tab || (!ignoreUpperCase && strings.isUpperAsciiLetter(chCode))) {
return i + 1;
}
if (ignoreUpperCase && strings.isLowerAsciiLetter(chCode)) {
return i; // multiple UPPERCase : assume an upper case word and a CamelCase word - like DSLModel
}
ignoreUpperCase = ignoreUpperCase && strings.isUpperAsciiLetter(chCode);
if (chCode === CharCode.Underline) {
return i + 2;
}
}
return str.length + 1;
}
export class WordPartOperations extends WordOperations {
public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
if (!selection.isEmpty()) {
return selection;
}
const position = new Position(selection.positionLineNumber, selection.positionColumn);
const lineNumber = position.lineNumber;
const column = position.column;
if (lineNumber === 1 && column === 1) {
// Ignore deleting at beginning of file
return null;
}
if (whitespaceHeuristics) {
let r = WordOperations._deleteWordLeftWhitespace(model, position);
if (r) {
return r;
}
}
const wordRange = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(position.lineNumber), position.column - 2);
const wordPartRange = new Range(lineNumber, column, lineNumber, lastWordPartEnd + 2);
if (wordPartRange.getStartPosition().isBeforeOrEqual(wordRange.getStartPosition())) {
return wordRange;
}
return wordPartRange;
}
public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
if (!selection.isEmpty()) {
return selection;
}
const position = new Position(selection.positionLineNumber, selection.positionColumn);
const lineNumber = position.lineNumber;
const column = position.column;
const lineCount = model.getLineCount();
const maxColumn = model.getLineMaxColumn(lineNumber);
if (lineNumber === lineCount && column === maxColumn) {
// Ignore deleting at end of file
return null;
}
if (whitespaceHeuristics) {
let r = WordOperations._deleteWordRightWhitespace(model, position);
if (r) {
return r;
}
}
const wordRange = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(position.lineNumber), position.column);
const wordPartRange = new Range(lineNumber, column, lineNumber, nextWordPartBegin);
if (wordRange.getEndPosition().isBeforeOrEqual(wordPartRange.getEndPosition())) {
return wordRange;
}
return wordPartRange;
}
public static moveWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position {
const lineNumber = position.lineNumber;
const column = position.column;
if (column === 1) {
return (lineNumber > 1 ? new Position(lineNumber - 1, model.getLineMaxColumn(lineNumber - 1)) : position);
}
const wordPos = WordOperations.moveWordLeft(wordSeparators, model, position, wordNavigationType);
const lastWordPartEnd = _lastWordPartEnd(model.getLineContent(lineNumber), column - 2);
const wordPartPos = new Position(lineNumber, lastWordPartEnd + 2);
if (wordPartPos.isBeforeOrEqual(wordPos)) {
return wordPos;
}
return wordPartPos;
}
public static moveWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position, wordNavigationType: WordNavigationType): Position {
const lineNumber = position.lineNumber;
const column = position.column;
const maxColumn = model.getLineMaxColumn(lineNumber);
if (column === maxColumn) {
return (lineNumber < model.getLineCount() ? new Position(lineNumber + 1, 1) : position);
}
const wordPos = WordOperations.moveWordRight(wordSeparators, model, position, wordNavigationType);
const nextWordPartBegin = _nextWordPartBegin(model.getLineContent(lineNumber), column);
const wordPartPos = new Position(lineNumber, nextWordPartBegin);
if (wordPos.isBeforeOrEqual(wordPartPos)) {
return wordPos;
}
return wordPartPos;
}
}

View File

@@ -332,6 +332,24 @@ export class Range {
return true;
}
/**
* Test if the two ranges are intersecting. If the ranges are touching it returns true.
*/
public static areIntersecting(a: IRange, b: IRange): boolean {
// Check if `a` is before `b`
if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn <= b.startColumn)) {
return false;
}
// Check if `b` is before `a`
if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn <= a.startColumn)) {
return false;
}
// These ranges must intersect
return true;
}
/**
* A function that compares ranges, useful for sorting ranges
* It will first compare ranges on the startPosition and then on the endPosition

View File

@@ -11,87 +11,51 @@ import { ICharChange, ILineChange } from 'vs/editor/common/editorCommon';
const MAXIMUM_RUN_TIME = 5000; // 5 seconds
const MINIMUM_MATCHING_CHARACTER_LENGTH = 3;
interface IMarker {
lineNumber: number;
column: number;
offset: number;
}
function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: () => boolean, pretty: boolean): IDiffChange[] {
const diffAlgo = new LcsDiff(originalSequence, modifiedSequence, continueProcessingPredicate);
return diffAlgo.ComputeDiff(pretty);
}
class MarkerSequence implements ISequence {
class LineMarkerSequence implements ISequence {
public buffer: string;
public startMarkers: IMarker[];
public endMarkers: IMarker[];
private readonly _lines: string[];
private readonly _startColumns: number[];
private readonly _endColumns: number[];
constructor(buffer: string, startMarkers: IMarker[], endMarkers: IMarker[]) {
this.buffer = buffer;
this.startMarkers = startMarkers;
this.endMarkers = endMarkers;
constructor(lines: string[]) {
let startColumns: number[] = [];
let endColumns: number[] = [];
for (let i = 0, length = lines.length; i < length; i++) {
startColumns[i] = LineMarkerSequence._getFirstNonBlankColumn(lines[i], 1);
endColumns[i] = LineMarkerSequence._getLastNonBlankColumn(lines[i], 1);
}
this._lines = lines;
this._startColumns = startColumns;
this._endColumns = endColumns;
}
public getLength(): number {
return this.startMarkers.length;
return this._lines.length;
}
public getElementHash(i: number): string {
return this.buffer.substring(this.startMarkers[i].offset, this.endMarkers[i].offset);
public getElementAtIndex(i: number): string {
return this._lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1);
}
public getStartLineNumber(i: number): number {
if (i === this.startMarkers.length) {
// This is the special case where a change happened after the last marker
return this.startMarkers[i - 1].lineNumber + 1;
}
return this.startMarkers[i].lineNumber;
return i + 1;
}
public getStartColumn(i: number): number {
return this.startMarkers[i].column;
return this._startColumns[i];
}
public getEndLineNumber(i: number): number {
return this.endMarkers[i].lineNumber;
return i + 1;
}
public getEndColumn(i: number): number {
return this.endMarkers[i].column;
}
}
class LineMarkerSequence extends MarkerSequence {
constructor(lines: string[]) {
let buffer = '';
let startMarkers: IMarker[] = [];
let endMarkers: IMarker[] = [];
for (let pos = 0, i = 0, length = lines.length; i < length; i++) {
buffer += lines[i];
const startColumn = LineMarkerSequence._getFirstNonBlankColumn(lines[i], 1);
const endColumn = LineMarkerSequence._getLastNonBlankColumn(lines[i], 1);
startMarkers.push({
offset: pos + startColumn - 1,
lineNumber: i + 1,
column: startColumn
});
endMarkers.push({
offset: pos + endColumn - 1,
lineNumber: i + 1,
column: endColumn
});
pos += lines[i].length;
}
super(buffer, startMarkers, endMarkers);
return this._endColumns[i];
}
public static _getFirstNonBlankColumn(txt: string, defaultValue: number): number {
@@ -110,26 +74,60 @@ class LineMarkerSequence extends MarkerSequence {
return r + 2;
}
public getCharSequence(startIndex: number, endIndex: number): MarkerSequence {
let startMarkers: IMarker[] = [];
let endMarkers: IMarker[] = [];
public getCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence {
let charCodes: number[] = [];
let lineNumbers: number[] = [];
let columns: number[] = [];
let len = 0;
for (let index = startIndex; index <= endIndex; index++) {
const startMarker = this.startMarkers[index];
const endMarker = this.endMarkers[index];
for (let i = startMarker.offset; i < endMarker.offset; i++) {
startMarkers.push({
offset: i,
lineNumber: startMarker.lineNumber,
column: startMarker.column + (i - startMarker.offset)
});
endMarkers.push({
offset: i + 1,
lineNumber: startMarker.lineNumber,
column: startMarker.column + (i - startMarker.offset) + 1
});
const lineContent = this._lines[index];
const startColumn = (shouldIgnoreTrimWhitespace ? this._startColumns[index] : 1);
const endColumn = (shouldIgnoreTrimWhitespace ? this._endColumns[index] : lineContent.length + 1);
for (let col = startColumn; col < endColumn; col++) {
charCodes[len] = lineContent.charCodeAt(col - 1);
lineNumbers[len] = index + 1;
columns[len] = col;
len++;
}
}
return new MarkerSequence(this.buffer, startMarkers, endMarkers);
return new CharSequence(charCodes, lineNumbers, columns);
}
}
class CharSequence implements ISequence {
private readonly _charCodes: number[];
private readonly _lineNumbers: number[];
private readonly _columns: number[];
constructor(charCodes: number[], lineNumbers: number[], columns: number[]) {
this._charCodes = charCodes;
this._lineNumbers = lineNumbers;
this._columns = columns;
}
public getLength(): number {
return this._charCodes.length;
}
public getElementAtIndex(i: number): number {
return this._charCodes[i];
}
public getStartLineNumber(i: number): number {
return this._lineNumbers[i];
}
public getStartColumn(i: number): number {
return this._columns[i];
}
public getEndLineNumber(i: number): number {
return this._lineNumbers[i];
}
public getEndColumn(i: number): number {
return this._columns[i] + 1;
}
}
@@ -165,7 +163,7 @@ class CharChange implements ICharChange {
this.modifiedEndColumn = modifiedEndColumn;
}
public static createFromDiffChange(diffChange: IDiffChange, originalCharSequence: MarkerSequence, modifiedCharSequence: MarkerSequence): CharChange {
public static createFromDiffChange(diffChange: IDiffChange, originalCharSequence: CharSequence, modifiedCharSequence: CharSequence): CharChange {
let originalStartLineNumber: number;
let originalStartColumn: number;
let originalEndLineNumber: number;
@@ -257,7 +255,7 @@ class LineChange implements ILineChange {
this.charChanges = charChanges;
}
public static createFromDiffResult(diffChange: IDiffChange, originalLineSequence: LineMarkerSequence, modifiedLineSequence: LineMarkerSequence, continueProcessingPredicate: () => boolean, shouldPostProcessCharChanges: boolean): LineChange {
public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineMarkerSequence, modifiedLineSequence: LineMarkerSequence, continueProcessingPredicate: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange {
let originalStartLineNumber: number;
let originalEndLineNumber: number;
let modifiedStartLineNumber: number;
@@ -280,9 +278,9 @@ class LineChange implements ILineChange {
modifiedEndLineNumber = modifiedLineSequence.getEndLineNumber(diffChange.modifiedStart + diffChange.modifiedLength - 1);
}
if (diffChange.originalLength !== 0 && diffChange.modifiedLength !== 0 && continueProcessingPredicate()) {
const originalCharSequence = originalLineSequence.getCharSequence(diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1);
const modifiedCharSequence = modifiedLineSequence.getCharSequence(diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1);
if (shouldComputeCharChanges && diffChange.originalLength !== 0 && diffChange.modifiedLength !== 0 && continueProcessingPredicate()) {
const originalCharSequence = originalLineSequence.getCharSequence(shouldIgnoreTrimWhitespace, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1);
const modifiedCharSequence = modifiedLineSequence.getCharSequence(shouldIgnoreTrimWhitespace, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1);
let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueProcessingPredicate, true);
@@ -301,6 +299,7 @@ class LineChange implements ILineChange {
}
export interface IDiffComputerOpts {
shouldComputeCharChanges: boolean;
shouldPostProcessCharChanges: boolean;
shouldIgnoreTrimWhitespace: boolean;
shouldMakePrettyDiff: boolean;
@@ -308,6 +307,7 @@ export interface IDiffComputerOpts {
export class DiffComputer {
private readonly shouldComputeCharChanges: boolean;
private readonly shouldPostProcessCharChanges: boolean;
private readonly shouldIgnoreTrimWhitespace: boolean;
private readonly shouldMakePrettyDiff: boolean;
@@ -320,6 +320,7 @@ export class DiffComputer {
private computationStartTime: number;
constructor(originalLines: string[], modifiedLines: string[], opts: IDiffComputerOpts) {
this.shouldComputeCharChanges = opts.shouldComputeCharChanges;
this.shouldPostProcessCharChanges = opts.shouldPostProcessCharChanges;
this.shouldIgnoreTrimWhitespace = opts.shouldIgnoreTrimWhitespace;
this.shouldMakePrettyDiff = opts.shouldMakePrettyDiff;
@@ -332,7 +333,7 @@ export class DiffComputer {
public computeDiff(): ILineChange[] {
if (this.original.getLength() === 1 && this.original.getElementHash(0).length === 0) {
if (this.original.getLength() === 1 && this.original.getElementAtIndex(0).length === 0) {
// empty original => fast path
return [{
originalStartLineNumber: 1,
@@ -352,7 +353,7 @@ export class DiffComputer {
}];
}
if (this.modified.getLength() === 1 && this.modified.getElementHash(0).length === 0) {
if (this.modified.getLength() === 1 && this.modified.getElementAtIndex(0).length === 0) {
// empty modified => fast path
return [{
originalStartLineNumber: 1,
@@ -382,7 +383,7 @@ export class DiffComputer {
if (this.shouldIgnoreTrimWhitespace) {
let lineChanges: LineChange[] = [];
for (let i = 0, length = rawChanges.length; i < length; i++) {
lineChanges.push(LineChange.createFromDiffResult(rawChanges[i], this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldPostProcessCharChanges));
lineChanges.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, rawChanges[i], this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
}
return lineChanges;
}
@@ -457,7 +458,7 @@ export class DiffComputer {
if (nextChange) {
// Emit the actual change
result.push(LineChange.createFromDiffResult(nextChange, this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldPostProcessCharChanges));
result.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, nextChange, this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
originalLineIndex += nextChange.originalLength;
modifiedLineIndex += nextChange.modifiedLength;
@@ -477,15 +478,17 @@ export class DiffComputer {
return;
}
let charChanges: CharChange[];
if (this.shouldComputeCharChanges) {
charChanges = [new CharChange(
originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
)];
}
result.push(new LineChange(
originalLineNumber, originalLineNumber,
modifiedLineNumber, modifiedLineNumber,
[
new CharChange(
originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
)
]
charChanges
));
}
@@ -509,10 +512,12 @@ export class DiffComputer {
if (prevChange.originalEndLineNumber + 1 === originalLineNumber && prevChange.modifiedEndLineNumber + 1 === modifiedLineNumber) {
prevChange.originalEndLineNumber = originalLineNumber;
prevChange.modifiedEndLineNumber = modifiedLineNumber;
prevChange.charChanges.push(new CharChange(
originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
));
if (this.shouldComputeCharChanges) {
prevChange.charChanges.push(new CharChange(
originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
));
}
return true;
}

View File

@@ -150,12 +150,16 @@ export interface ILineChange extends IChange {
/**
* @internal
*/
export interface IConfiguration {
export interface IConfiguration extends IDisposable {
onDidChange(listener: (e: editorOptions.IConfigurationChangedEvent) => void): IDisposable;
readonly editor: editorOptions.InternalEditorOptions;
setMaxLineNumber(maxLineNumber: number): void;
updateOptions(newOptions: editorOptions.IEditorOptions): void;
getRawOptions(): editorOptions.IEditorOptions;
observeReferenceElement(dimension?: IDimension): void;
setIsDominatedByLongLines(isDominatedByLongLines: boolean): void;
}
// --- view
@@ -287,9 +291,9 @@ export interface IEditor {
focus(): void;
/**
* Returns true if this editor has keyboard focus (e.g. cursor is blinking).
* Returns true if the text inside this editor is focused (i.e. cursor is blinking).
*/
isFocused(): boolean;
hasTextFocus(): boolean;
/**
* Returns all actions associated with this editor.
@@ -457,6 +461,29 @@ export interface IEditor {
changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => any): any;
}
/**
* A diff editor.
*
* @internal
*/
export interface IDiffEditor extends IEditor {
/**
* Type the getModel() of IEditor.
*/
getModel(): IDiffEditorModel;
/**
* Get the `original` editor.
*/
getOriginalEditor(): IEditor;
/**
* Get the `modified` editor.
*/
getModifiedEditor(): IEditor;
}
/**
* An editor contribution that gets created every time a new editor gets created and gets disposed when the editor gets disposed.
*/

View File

@@ -30,6 +30,8 @@ export namespace EditorContextKeys {
export const tabMovesFocus = new RawContextKey<boolean>('editorTabMovesFocus', false);
export const tabDoesNotMoveFocus: ContextKeyExpr = tabMovesFocus.toNegated();
export const isInEmbeddedEditor = new RawContextKey<boolean>('isInEmbeddedEditor', undefined);
export const canUndo = new RawContextKey<boolean>('canUndo', false);
export const canRedo = new RawContextKey<boolean>('canRedo', false);
// -- mode context keys
export const languageId = new RawContextKey<string>('editorLangId', undefined);

View File

@@ -596,11 +596,6 @@ export interface ITextModel {
*/
getEOL(): string;
/**
* Change the end of line sequence used in the text buffer.
*/
setEOL(eol: EndOfLineSequence): void;
/**
* Get the minimum legal column for line at `lineNumber`
*/
@@ -1008,6 +1003,12 @@ export interface ITextModel {
*/
pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[];
/**
* Change the end of line sequence. This is the preferred way of
* changing the eol sequence. This will land on the undo stack.
*/
pushEOL(eol: EndOfLineSequence): void;
/**
* Edit the model without adding the edits to the undo stack.
* This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way.
@@ -1016,6 +1017,12 @@ export interface ITextModel {
*/
applyEdits(operations: IIdentifiedSingleEditOperation[]): IIdentifiedSingleEditOperation[];
/**
* Change the end of line sequence without recording in the undo stack.
* This can have dire consequences on the undo stack! See @pushEOL for the preferred way.
*/
setEOL(eol: EndOfLineSequence): void;
/**
* Undo edit operations until the first previous stop point created by `pushStackElement`.
* The inverse edit operations will be pushed on the redo stack.
@@ -1023,6 +1030,12 @@ export interface ITextModel {
*/
undo(): Selection[];
/**
* Is there anything in the undo stack?
* @internal
*/
canUndo(): boolean;
/**
* Redo edit operations until the next stop point created by `pushStackElement`.
* The inverse edit operations will be pushed on the undo stack.
@@ -1030,6 +1043,12 @@ export interface ITextModel {
*/
redo(): Selection[];
/**
* Is there anything in the redo stack?
* @internal
*/
canRedo(): boolean;
/**
* @deprecated Please use `onDidChangeContent` instead.
* An event emitted when the contents of the model have changed.

View File

@@ -5,7 +5,7 @@
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ICursorStateComputer, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
import { ICursorStateComputer, IIdentifiedSingleEditOperation, EndOfLineSequence } from 'vs/editor/common/model';
import { Selection } from 'vs/editor/common/core/selection';
import { TextModel } from 'vs/editor/common/model/textModel';
@@ -14,13 +14,86 @@ interface IEditOperation {
}
interface IStackElement {
beforeVersionId: number;
beforeCursorState: Selection[];
readonly beforeVersionId: number;
readonly beforeCursorState: Selection[];
readonly afterCursorState: Selection[];
readonly afterVersionId: number;
editOperations: IEditOperation[];
undo(model: TextModel): void;
redo(model: TextModel): void;
}
afterCursorState: Selection[];
afterVersionId: number;
class EditStackElement implements IStackElement {
public readonly beforeVersionId: number;
public readonly beforeCursorState: Selection[];
public afterCursorState: Selection[];
public afterVersionId: number;
public editOperations: IEditOperation[];
constructor(beforeVersionId: number, beforeCursorState: Selection[]) {
this.beforeVersionId = beforeVersionId;
this.beforeCursorState = beforeCursorState;
this.afterCursorState = null;
this.afterVersionId = -1;
this.editOperations = [];
}
public undo(model: TextModel): void {
// Apply all operations in reverse order
for (let i = this.editOperations.length - 1; i >= 0; i--) {
this.editOperations[i] = {
operations: model.applyEdits(this.editOperations[i].operations)
};
}
}
public redo(model: TextModel): void {
// Apply all operations
for (let i = 0; i < this.editOperations.length; i++) {
this.editOperations[i] = {
operations: model.applyEdits(this.editOperations[i].operations)
};
}
}
}
function getModelEOL(model: TextModel): EndOfLineSequence {
const eol = model.getEOL();
if (eol === '\n') {
return EndOfLineSequence.LF;
} else {
return EndOfLineSequence.CRLF;
}
}
class EOLStackElement implements IStackElement {
public readonly beforeVersionId: number;
public readonly beforeCursorState: Selection[];
public readonly afterCursorState: Selection[];
public afterVersionId: number;
public eol: EndOfLineSequence;
constructor(beforeVersionId: number, setEOL: EndOfLineSequence) {
this.beforeVersionId = beforeVersionId;
this.beforeCursorState = null;
this.afterCursorState = null;
this.afterVersionId = -1;
this.eol = setEOL;
}
public undo(model: TextModel): void {
let redoEOL = getModelEOL(model);
model.setEOL(this.eol);
this.eol = redoEOL;
}
public redo(model: TextModel): void {
let undoEOL = getModelEOL(model);
model.setEOL(this.eol);
this.eol = undoEOL;
}
}
export interface IUndoRedoResult {
@@ -55,34 +128,60 @@ export class EditStack {
this.future = [];
}
public pushEOL(eol: EndOfLineSequence): void {
// No support for parallel universes :(
this.future = [];
if (this.currentOpenStackElement) {
this.pushStackElement();
}
const prevEOL = getModelEOL(this.model);
let stackElement = new EOLStackElement(this.model.getAlternativeVersionId(), prevEOL);
this.model.setEOL(eol);
stackElement.afterVersionId = this.model.getVersionId();
this.currentOpenStackElement = stackElement;
this.pushStackElement();
}
public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] {
// No support for parallel universes :(
this.future = [];
let stackElement: EditStackElement = null;
if (this.currentOpenStackElement) {
if (this.currentOpenStackElement instanceof EditStackElement) {
stackElement = this.currentOpenStackElement;
} else {
this.pushStackElement();
}
}
if (!this.currentOpenStackElement) {
this.currentOpenStackElement = {
beforeVersionId: this.model.getAlternativeVersionId(),
beforeCursorState: beforeCursorState,
editOperations: [],
afterCursorState: null,
afterVersionId: -1
};
stackElement = new EditStackElement(this.model.getAlternativeVersionId(), beforeCursorState);
this.currentOpenStackElement = stackElement;
}
const inverseEditOperation: IEditOperation = {
operations: this.model.applyEdits(editOperations)
};
this.currentOpenStackElement.editOperations.push(inverseEditOperation);
stackElement.editOperations.push(inverseEditOperation);
stackElement.afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperation.operations);
stackElement.afterVersionId = this.model.getVersionId();
return stackElement.afterCursorState;
}
private static _computeCursorState(cursorStateComputer: ICursorStateComputer, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] {
try {
this.currentOpenStackElement.afterCursorState = cursorStateComputer ? cursorStateComputer(inverseEditOperation.operations) : null;
return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null;
} catch (e) {
onUnexpectedError(e);
this.currentOpenStackElement.afterCursorState = null;
return null;
}
this.currentOpenStackElement.afterVersionId = this.model.getVersionId();
return this.currentOpenStackElement.afterCursorState;
}
public undo(): IUndoRedoResult {
@@ -93,13 +192,9 @@ export class EditStack {
const pastStackElement = this.past.pop();
try {
// Apply all operations in reverse order
for (let i = pastStackElement.editOperations.length - 1; i >= 0; i--) {
pastStackElement.editOperations[i] = {
operations: this.model.applyEdits(pastStackElement.editOperations[i].operations)
};
}
pastStackElement.undo(this.model);
} catch (e) {
onUnexpectedError(e);
this.clear();
return null;
}
@@ -115,23 +210,19 @@ export class EditStack {
return null;
}
public canUndo(): boolean {
return (this.past.length > 0);
}
public redo(): IUndoRedoResult {
if (this.future.length > 0) {
if (this.currentOpenStackElement) {
throw new Error('How is this possible?');
}
const futureStackElement = this.future.pop();
try {
// Apply all operations
for (let i = 0; i < futureStackElement.editOperations.length; i++) {
futureStackElement.editOperations[i] = {
operations: this.model.applyEdits(futureStackElement.editOperations[i].operations)
};
}
futureStackElement.redo(this.model);
} catch (e) {
onUnexpectedError(e);
this.clear();
return null;
}
@@ -146,4 +237,8 @@ export class EditStack {
return null;
}
public canRedo(): boolean {
return (this.future.length > 0);
}
}

View File

@@ -99,21 +99,17 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de
for (let lineNumber = 1; lineNumber <= linesCount; lineNumber++) {
let currentLineLength = source.getLineLength(lineNumber);
let currentLineText = source.getLineContent(lineNumber);
let charCodeAt: (offset: number) => number;
if (currentLineLength > 65536) {
// if the text buffer is chunk based, so long lines are cons-string, v8 will flattern the string when we check charCode.
// checking charCode on chunks directly is cheaper.
charCodeAt = (offset: number) => source.getLineCharCode(lineNumber, offset);
} else {
charCodeAt = (offset: number) => currentLineText.charCodeAt(offset);
}
// if the text buffer is chunk based, so long lines are cons-string, v8 will flattern the string when we check charCode.
// checking charCode on chunks directly is cheaper.
const useCurrentLineText = (currentLineLength <= 65536);
let currentLineHasContent = false; // does `currentLineText` contain non-whitespace chars
let currentLineIndentation = 0; // index at which `currentLineText` contains the first non-whitespace char
let currentLineSpacesCount = 0; // count of spaces found in `currentLineText` indentation
let currentLineTabsCount = 0; // count of tabs found in `currentLineText` indentation
for (let j = 0, lenJ = currentLineLength; j < lenJ; j++) {
let charCode = charCodeAt(j);
let charCode = (useCurrentLineText ? currentLineText.charCodeAt(j) : source.getLineCharCode(lineNumber, j));
if (charCode === CharCode.Tab) {
currentLineTabsCount++;
@@ -147,12 +143,6 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de
previousLineIndentation = currentLineIndentation;
}
// Take into account the last line as well
let deltaSpacesCount = spacesDiff(previousLineText, previousLineIndentation, '', 0);
if (deltaSpacesCount <= MAX_ALLOWED_TAB_SIZE_GUESS) {
spacesDiffCount[deltaSpacesCount]++;
}
let insertSpaces = defaultInsertSpaces;
if (linesIndentedWithTabsCount !== linesIndentedWithSpacesCount) {
insertSpaces = (linesIndentedWithTabsCount < linesIndentedWithSpacesCount);

View File

@@ -6,18 +6,20 @@
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
import { Range } from 'vs/editor/common/core/range';
import { IModelDecoration } from 'vs/editor/common/model';
import { IModelDecoration, TrackedRangeStickiness as ActualTrackedRangeStickiness } from 'vs/editor/common/model';
//
// The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
//
export const ClassName = {
EditorHintDecoration: 'squiggly-hint',
EditorInfoDecoration: 'squiggly-info',
EditorWarningDecoration: 'squiggly-warning',
EditorErrorDecoration: 'squiggly-error'
};
export const enum ClassName {
EditorHintDecoration = 'squiggly-hint',
EditorInfoDecoration = 'squiggly-info',
EditorWarningDecoration = 'squiggly-warning',
EditorErrorDecoration = 'squiggly-error',
EditorUnnecessaryDecoration = 'squiggly-unnecessary',
EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary'
}
/**
* Describes the behavior of decorations when typing/editing near their edges.
@@ -114,11 +116,14 @@ function setNodeIsInOverviewRuler(node: IntervalNode, value: boolean): void {
function getNodeStickiness(node: IntervalNode): TrackedRangeStickiness {
return ((node.metadata & Constants.StickinessMask) >>> Constants.StickinessOffset);
}
function setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickiness): void {
function _setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickiness): void {
node.metadata = (
(node.metadata & Constants.StickinessMaskInverse) | (stickiness << Constants.StickinessOffset)
);
}
export function setNodeStickiness(node: IntervalNode, stickiness: ActualTrackedRangeStickiness): void {
_setNodeStickiness(node, <number>stickiness);
}
export class IntervalNode implements IModelDecoration {
@@ -163,7 +168,7 @@ export class IntervalNode implements IModelDecoration {
this.ownerId = 0;
this.options = null;
setNodeIsForValidation(this, false);
setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
_setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
setNodeIsInOverviewRuler(this, false);
this.cachedVersionId = 0;
@@ -192,7 +197,7 @@ export class IntervalNode implements IModelDecoration {
|| className === ClassName.EditorWarningDecoration
|| className === ClassName.EditorInfoDecoration
));
setNodeStickiness(this, <number>this.options.stickiness);
_setNodeStickiness(this, <number>this.options.stickiness);
setNodeIsInOverviewRuler(this, this.options.overviewRuler.color ? true : false);
}
@@ -391,7 +396,7 @@ function adjustMarkerBeforeColumn(markerOffset: number, markerStickToPreviousCha
* This is a lot more complicated than strictly necessary to maintain the same behaviour
* as when decorations were implemented using two markers.
*/
function nodeAcceptEdit(node: IntervalNode, start: number, end: number, textLength: number, forceMoveMarkers: boolean): void {
export function nodeAcceptEdit(node: IntervalNode, start: number, end: number, textLength: number, forceMoveMarkers: boolean): void {
const nodeStickiness = getNodeStickiness(node);
const startStickToPreviousCharacter = (
nodeStickiness === TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges

View File

@@ -517,7 +517,7 @@ export class PieceTreeBase {
return this._lineCnt;
}
public getLineContent(lineNumber): string {
public getLineContent(lineNumber: number): string {
if (this._lastVisitedLine.lineNumber === lineNumber) {
return this._lastVisitedLine.value;
}

View File

@@ -78,17 +78,17 @@ export class TreeNode {
}
}
export const enum NodeColor {
Black = 0,
Red = 1,
}
export const SENTINEL: TreeNode = new TreeNode(null, NodeColor.Black);
SENTINEL.parent = SENTINEL;
SENTINEL.left = SENTINEL;
SENTINEL.right = SENTINEL;
SENTINEL.color = NodeColor.Black;
export const enum NodeColor {
Black = 0,
Red = 1,
}
export function leftest(node: TreeNode): TreeNode {
while (node.left !== SENTINEL) {
node = node.left;

View File

@@ -46,7 +46,7 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory
}
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): TPromise<model.ITextBufferFactory> {
return new TPromise<model.ITextBufferFactory>((c, e, p) => {
return new TPromise<model.ITextBufferFactory>((c, e) => {
let done = false;
let builder = createTextBufferBuilder();
@@ -898,6 +898,9 @@ export class TextModel extends Disposable implements model.ITextModel {
* @param strict Do NOT allow a position inside a high-low surrogate pair
*/
private _isValidPosition(lineNumber: number, column: number, strict: boolean): boolean {
if (isNaN(lineNumber)) {
return false;
}
if (lineNumber < 1) {
return false;
@@ -908,6 +911,10 @@ export class TextModel extends Disposable implements model.ITextModel {
return false;
}
if (isNaN(column)) {
return false;
}
if (column < 1) {
return false;
}
@@ -933,8 +940,8 @@ export class TextModel extends Disposable implements model.ITextModel {
* @param strict Do NOT allow a position inside a high-low surrogate pair
*/
private _validatePosition(_lineNumber: number, _column: number, strict: boolean): Position {
const lineNumber = Math.floor(typeof _lineNumber === 'number' ? _lineNumber : 1);
const column = Math.floor(typeof _column === 'number' ? _column : 1);
const lineNumber = Math.floor((typeof _lineNumber === 'number' && !isNaN(_lineNumber)) ? _lineNumber : 1);
const column = Math.floor((typeof _column === 'number' && !isNaN(_column)) ? _column : 1);
const lineCount = this._buffer.getLineCount();
if (lineNumber < 1) {
@@ -1143,6 +1150,21 @@ export class TextModel extends Disposable implements model.ITextModel {
this._commandManager.pushStackElement();
}
public pushEOL(eol: model.EndOfLineSequence): void {
const currentEOL = (this.getEOL() === '\n' ? model.EndOfLineSequence.LF : model.EndOfLineSequence.CRLF);
if (currentEOL === eol) {
return;
}
try {
this._onDidChangeDecorations.beginDeferredEmit();
this._eventEmitter.beginDeferredEmit();
this._commandManager.pushEOL(eol);
} finally {
this._eventEmitter.endDeferredEmit();
this._onDidChangeDecorations.endDeferredEmit();
}
}
public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] {
try {
this._onDidChangeDecorations.beginDeferredEmit();
@@ -1213,6 +1235,14 @@ export class TextModel extends Disposable implements model.ITextModel {
continue;
}
if (
trimLineNumber === editRange.startLineNumber && editRange.startColumn === 1
&& editRange.isEmpty() && editText && editText.length > 0 && editText.charAt(editText.length - 1) === '\n'
) {
// This edit inserts a new line (and maybe other text) before `trimLine`
continue;
}
// Looks like we can't trim this line as it would interfere with an incoming edit
allowTrimLine = false;
break;
@@ -1386,6 +1416,10 @@ export class TextModel extends Disposable implements model.ITextModel {
}
}
public canUndo(): boolean {
return this._commandManager.canUndo();
}
private _redo(): Selection[] {
this._isRedoing = true;
let r = this._commandManager.redo();
@@ -1411,6 +1445,10 @@ export class TextModel extends Disposable implements model.ITextModel {
}
}
public canRedo(): boolean {
return this._commandManager.canRedo();
}
//#endregion
//#region Decorations
@@ -2482,10 +2520,10 @@ export class TextModel extends Disposable implements model.ITextModel {
const upLineNumber = lineNumber - distance;
const downLineNumber = lineNumber + distance;
if (upLineNumber < 1 || upLineNumber < minLineNumber) {
if (distance !== 0 && (upLineNumber < 1 || upLineNumber < minLineNumber)) {
goUp = false;
}
if (downLineNumber > lineCount || downLineNumber > maxLineNumber) {
if (distance !== 0 && (downLineNumber > lineCount || downLineNumber > maxLineNumber)) {
goDown = false;
}
if (distance > 50000) {
@@ -2734,7 +2772,7 @@ class DecorationsTrees {
}
function cleanClassName(className: string): string {
return className.replace(/[^a-z0-9\-]/gi, ' ');
return className.replace(/[^a-z0-9\-_]/gi, ' ');
}
export class ModelDecorationOverviewRulerOptions implements model.IModelDecorationOverviewRulerOptions {

View File

@@ -127,6 +127,55 @@ export function createFindMatch(range: Range, rawMatches: RegExpExecArray, captu
return new FindMatch(range, matches);
}
class LineFeedCounter {
private readonly _lineFeedsOffsets: number[];
constructor(text: string) {
let lineFeedsOffsets: number[] = [];
let lineFeedsOffsetsLen = 0;
for (let i = 0, textLen = text.length; i < textLen; i++) {
if (text.charCodeAt(i) === CharCode.LineFeed) {
lineFeedsOffsets[lineFeedsOffsetsLen++] = i;
}
}
this._lineFeedsOffsets = lineFeedsOffsets;
}
public findLineFeedCountBeforeOffset(offset: number): number {
const lineFeedsOffsets = this._lineFeedsOffsets;
let min = 0;
let max = lineFeedsOffsets.length - 1;
if (max === -1) {
// no line feeds
return 0;
}
if (offset <= lineFeedsOffsets[0]) {
// before first line feed
return 0;
}
while (min < max) {
const mid = min + ((max - min) / 2 >> 0);
if (lineFeedsOffsets[mid] >= offset) {
max = mid - 1;
} else {
if (lineFeedsOffsets[mid + 1] >= offset) {
// bingo!
min = mid;
max = mid;
} else {
min = mid + 1;
}
}
}
return min + 1;
}
}
export class TextModelSearch {
public static findMatches(model: TextModel, searchParams: SearchParams, searchRange: Range, captureMatches: boolean, limitResultCount: number): FindMatch[] {
@@ -136,23 +185,6 @@ export class TextModelSearch {
}
if (searchData.regex.multiline) {
if (searchData.regex.source === '\\n') {
// Fast path for searching for EOL
let result: FindMatch[] = [], resultLen = 0;
for (let lineNumber = 1, lineCount = model.getLineCount(); lineNumber < lineCount; lineNumber++) {
const range = new Range(lineNumber, model.getLineMaxColumn(lineNumber), lineNumber + 1, 1);
if (captureMatches) {
result[resultLen++] = new FindMatch(range, null);
} else {
result[resultLen++] = new FindMatch(range, ['\n']);
}
if (resultLen >= limitResultCount) {
break;
}
}
return result;
}
return this._doFindMatchesMultiline(model, searchRange, new Searcher(searchData.wordSeparators, searchData.regex), captureMatches, limitResultCount);
}
return this._doFindMatchesLineByLine(model, searchRange, searchData, captureMatches, limitResultCount);
@@ -162,16 +194,11 @@ export class TextModelSearch {
* Multiline search always executes on the lines concatenated with \n.
* We must therefore compensate for the count of \n in case the model is CRLF
*/
private static _getMultilineMatchRange(model: TextModel, deltaOffset: number, text: string, matchIndex: number, match0: string): Range {
private static _getMultilineMatchRange(model: TextModel, deltaOffset: number, text: string, lfCounter: LineFeedCounter, matchIndex: number, match0: string): Range {
let startOffset: number;
let lineFeedCountBeforeMatch = 0;
if (model.getEOL() === '\r\n') {
let lineFeedCountBeforeMatch = 0;
for (let i = 0; i < matchIndex; i++) {
let chCode = text.charCodeAt(i);
if (chCode === CharCode.LineFeed) {
lineFeedCountBeforeMatch++;
}
}
lineFeedCountBeforeMatch = lfCounter.findLineFeedCountBeforeOffset(matchIndex);
startOffset = deltaOffset + matchIndex + lineFeedCountBeforeMatch /* add as many \r as there were \n */;
} else {
startOffset = deltaOffset + matchIndex;
@@ -179,13 +206,8 @@ export class TextModelSearch {
let endOffset: number;
if (model.getEOL() === '\r\n') {
let lineFeedCountInMatch = 0;
for (let i = 0, len = match0.length; i < len; i++) {
let chCode = text.charCodeAt(i + matchIndex);
if (chCode === CharCode.LineFeed) {
lineFeedCountInMatch++;
}
}
let lineFeedCountBeforeEndOfMatch = lfCounter.findLineFeedCountBeforeOffset(matchIndex + match0.length);
let lineFeedCountInMatch = lineFeedCountBeforeEndOfMatch - lineFeedCountBeforeMatch;
endOffset = startOffset + match0.length + lineFeedCountInMatch /* add as many \r as there were \n */;
} else {
endOffset = startOffset + match0.length;
@@ -202,6 +224,7 @@ export class TextModelSearch {
// This makes it that \n will match the EOL for both CRLF and LF models
// We compensate for offset errors in `_getMultilineMatchRange`
const text = model.getValueInRange(searchRange, EndOfLinePreference.LF);
const lfCounter = (model.getEOL() === '\r\n' ? new LineFeedCounter(text) : null);
const result: FindMatch[] = [];
let counter = 0;
@@ -209,7 +232,7 @@ export class TextModelSearch {
let m: RegExpExecArray;
searcher.reset(0);
while ((m = searcher.next(text))) {
result[counter++] = createFindMatch(this._getMultilineMatchRange(model, deltaOffset, text, m.index, m[0]), m, captureMatches);
result[counter++] = createFindMatch(this._getMultilineMatchRange(model, deltaOffset, text, lfCounter, m.index, m[0]), m, captureMatches);
if (counter >= limitResultCount) {
return result;
}
@@ -304,11 +327,12 @@ export class TextModelSearch {
// This makes it that \n will match the EOL for both CRLF and LF models
// We compensate for offset errors in `_getMultilineMatchRange`
const text = model.getValueInRange(new Range(searchTextStart.lineNumber, searchTextStart.column, lineCount, model.getLineMaxColumn(lineCount)), EndOfLinePreference.LF);
const lfCounter = (model.getEOL() === '\r\n' ? new LineFeedCounter(text) : null);
searcher.reset(searchStart.column - 1);
let m = searcher.next(text);
if (m) {
return createFindMatch(
this._getMultilineMatchRange(model, deltaOffset, text, m.index, m[0]),
this._getMultilineMatchRange(model, deltaOffset, text, lfCounter, m.index, m[0]),
m,
captureMatches
);

View File

@@ -18,6 +18,7 @@ import { Color } from 'vs/base/common/color';
import { IMarkerData } from 'vs/platform/markers/common/markers';
import * as model from 'vs/editor/common/model';
import { isObject } from 'vs/base/common/types';
import { Selection } from 'vs/editor/common/core/selection';
/**
* Open ended enum at runtime
@@ -292,6 +293,7 @@ export interface ISuggestion {
documentation?: string | IMarkdownString;
filterText?: string;
sortText?: string;
preselect?: boolean;
noAutoAccept?: boolean;
commitCharacters?: string[];
overwriteBefore?: number;
@@ -347,11 +349,20 @@ export interface CodeAction {
kind?: string;
}
/**
* @internal
*/
export enum CodeActionTrigger {
Automatic = 1,
Manual = 2,
}
/**
* @internal
*/
export interface CodeActionContext {
only?: string;
trigger: CodeActionTrigger;
}
/**
@@ -363,7 +374,7 @@ export interface CodeActionProvider {
/**
* Provide commands for the given document and range.
*/
provideCodeActions(model: model.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): CodeAction[] | Thenable<CodeAction[]>;
provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): CodeAction[] | Thenable<CodeAction[]>;
/**
* Optional list of of CodeActionKinds that this provider returns.
@@ -527,6 +538,13 @@ export interface Location {
*/
export type Definition = Location | Location[];
export interface DefinitionLink {
origin?: IRange;
uri: URI;
range: IRange;
selectionRange?: IRange;
}
/**
* The definition provider interface defines the contract between extensions and
* the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition)
@@ -536,7 +554,7 @@ export interface DefinitionProvider {
/**
* Provide the definition of the symbol at the given position and document.
*/
provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable<Definition>;
provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable<Definition | DefinitionLink[]>;
}
/**
@@ -547,7 +565,7 @@ export interface ImplementationProvider {
/**
* Provide the implementation of the symbol at the given position and document.
*/
provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable<Definition>;
provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable<Definition | DefinitionLink[]>;
}
/**
@@ -558,7 +576,7 @@ export interface TypeDefinitionProvider {
/**
* Provide the type definition of the symbol at the given position and document.
*/
provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | Thenable<Definition>;
provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): Definition | DefinitionLink[] | Thenable<Definition | DefinitionLink[]>;
}
/**
@@ -628,47 +646,32 @@ export const symbolKindToCssClass = (function () {
_fromMapping[SymbolKind.TypeParameter] = 'type-parameter';
return function toCssClassName(kind: SymbolKind): string {
return _fromMapping[kind] || 'property';
return `symbol-icon ${_fromMapping[kind] || 'property'}`;
};
})();
/**
* @internal
*/
export interface IOutline {
entries: SymbolInformation[];
}
/**
* Represents information about programming constructs like variables, classes,
* interfaces etc.
*/
export interface SymbolInformation {
/**
* The name of this symbol.
*/
export interface DocumentSymbol {
name: string;
/**
* The name of the symbol containing this symbol.
*/
containerName?: string;
/**
* The kind of this symbol.
*/
detail: string;
kind: SymbolKind;
/**
* The location of this symbol.
*/
location: Location;
containerName?: string;
range: IRange;
selectionRange: IRange;
children?: DocumentSymbol[];
}
/**
* The document symbol provider interface defines the contract between extensions and
* the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_goto-symbol)-feature.
*/
export interface DocumentSymbolProvider {
displayName?: string;
/**
* Provide symbol information for the given document.
*/
provideDocumentSymbols(model: model.ITextModel, token: CancellationToken): SymbolInformation[] | Thenable<SymbolInformation[]>;
provideDocumentSymbols(model: model.ITextModel, token: CancellationToken): DocumentSymbol[] | Thenable<DocumentSymbol[]>;
}
export interface TextEdit {
@@ -903,6 +906,7 @@ export function isResourceTextEdit(thing: any): thing is ResourceTextEdit {
export interface ResourceFileEdit {
oldUri: URI;
newUri: URI;
options: { overwrite?: boolean, ignoreIfNotExists?: boolean, ignoreIfExists?: boolean, recursive?: boolean };
}
export interface ResourceTextEdit {
@@ -933,6 +937,81 @@ export interface Command {
tooltip?: string;
arguments?: any[];
}
export interface CommentInfo {
owner: number;
threads: CommentThread[];
commentingRanges?: IRange[];
reply?: Command;
}
export enum CommentThreadCollapsibleState {
/**
* Determines an item is collapsed
*/
Collapsed = 0,
/**
* Determines an item is expanded
*/
Expanded = 1
}
export interface CommentThread {
threadId: string;
resource: string;
range: IRange;
comments: Comment[];
collapsibleState?: CommentThreadCollapsibleState;
reply?: Command;
}
export interface NewCommentAction {
ranges: IRange[];
actions: Command[];
}
export interface Comment {
readonly commentId: string;
readonly body: IMarkdownString;
readonly userName: string;
readonly gravatar: string;
readonly command?: Command;
}
export interface CommentThreadChangedEvent {
readonly owner: number;
/**
* Added comment threads.
*/
readonly added: CommentThread[];
/**
* Removed comment threads.
*/
readonly removed: CommentThread[];
/**
* Changed comment threads.
*/
readonly changed: CommentThread[];
}
export interface DocumentCommentProvider {
provideDocumentComments(resource: URI, token: CancellationToken): Promise<CommentInfo>;
createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread>;
replyToCommentThread(resource: URI, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread>;
onDidChangeCommentThreads(): Event<CommentThreadChangedEvent>;
}
export interface WorkspaceCommentProvider {
provideWorkspaceComments(token: CancellationToken): Promise<CommentThread[]>;
createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread>;
replyToCommentThread(resource: URI, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread>;
onDidChangeCommentThreads(): Event<CommentThreadChangedEvent>;
}
export interface ICodeLensSymbol {
range: IRange;
id?: string;

View File

@@ -13,7 +13,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { ITextModel } from 'vs/editor/common/model';
import { onUnexpectedError } from 'vs/base/common/errors';
import * as strings from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { createScopedLineTokens } from 'vs/editor/common/modes/supports';
import { LineTokens } from 'vs/editor/common/core/lineTokens';
@@ -189,14 +189,12 @@ export class LanguageConfigurationRegistryImpl {
let current = new RichEditSupport(languageIdentifier, previous, configuration);
this._entries[languageIdentifier.id] = current;
this._onDidChange.fire({ languageIdentifier });
return {
dispose: () => {
if (this._entries[languageIdentifier.id] === current) {
this._entries[languageIdentifier.id] = previous;
this._onDidChange.fire({ languageIdentifier });
}
return toDisposable(() => {
if (this._entries[languageIdentifier.id] === current) {
this._entries[languageIdentifier.id] = previous;
this._onDidChange.fire({ languageIdentifier });
}
};
});
}
private _getRichEditSupport(languageId: LanguageId): RichEditSupport {

View File

@@ -6,7 +6,7 @@
'use strict';
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ITextModel } from 'vs/editor/common/model';
import { LanguageSelector, score } from 'vs/editor/common/modes/languageSelector';
import { shouldSynchronizeModel } from 'vs/editor/common/services/modelService';
@@ -54,19 +54,17 @@ export default class LanguageFeatureRegistry<T> {
this._lastCandidate = undefined;
this._onDidChange.fire(this._entries.length);
return {
dispose: () => {
if (entry) {
let idx = this._entries.indexOf(entry);
if (idx >= 0) {
this._entries.splice(idx, 1);
this._lastCandidate = undefined;
this._onDidChange.fire(this._entries.length);
entry = undefined;
}
return toDisposable(() => {
if (entry) {
let idx = this._entries.indexOf(entry);
if (idx >= 0) {
this._entries.splice(idx, 1);
this._lastCandidate = undefined;
this._onDidChange.fire(this._entries.length);
entry = undefined;
}
}
};
});
}
has(model: ITextModel): boolean {

View File

@@ -186,6 +186,9 @@ export class BracketsUtils {
let matchOffset = m.index;
let matchLength = m[0].length;
if (matchLength === 0) {
return null;
}
let absoluteMatchOffset = offset + matchOffset;
return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength);

View File

@@ -99,7 +99,7 @@ export function parseTokenTheme(source: ITokenThemeRule[]): ParsedTokenThemeRule
/**
* Resolve rules (i.e. inheritance).
*/
function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[]): TokenTheme {
function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[], customTokenColors: string[]): TokenTheme {
// Sort rules lexicographically, and then by index if necessary
parsedThemeRules.sort((a, b) => {
@@ -127,9 +127,17 @@ function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[]):
}
}
let colorMap = new ColorMap();
// ensure default foreground gets id 1 and default background gets id 2
let defaults = new ThemeTrieElementRule(defaultFontStyle, colorMap.getId(defaultForeground), colorMap.getId(defaultBackground));
// start with token colors from custom token themes
for (let color of customTokenColors) {
colorMap.getId(color);
}
let foregroundColorId = colorMap.getId(defaultForeground);
let backgroundColorId = colorMap.getId(defaultBackground);
let defaults = new ThemeTrieElementRule(defaultFontStyle, foregroundColorId, backgroundColorId);
let root = new ThemeTrieElement(defaults);
for (let i = 0, len = parsedThemeRules.length; i < len; i++) {
let rule = parsedThemeRules[i];
@@ -139,6 +147,8 @@ function resolveParsedTokenThemeRules(parsedThemeRules: ParsedTokenThemeRule[]):
return new TokenTheme(colorMap, root);
}
const colorRegExp = /^#?([0-9A-Fa-f]{6})([0-9A-Fa-f]{2})?$/;
export class ColorMap {
private _lastColorId: number;
@@ -155,10 +165,11 @@ export class ColorMap {
if (color === null) {
return 0;
}
color = color.toUpperCase();
if (!/^[0-9A-F]{6}$/.test(color)) {
throw new Error('Illegal color name: ' + color);
const match = color.match(colorRegExp);
if (!match) {
throw new Error('Illegal value for token color: ' + color);
}
color = match[1].toUpperCase();
let value = this._color2id.get(color);
if (value) {
return value;
@@ -177,12 +188,12 @@ export class ColorMap {
export class TokenTheme {
public static createFromRawTokenTheme(source: ITokenThemeRule[]): TokenTheme {
return this.createFromParsedTokenTheme(parseTokenTheme(source));
public static createFromRawTokenTheme(source: ITokenThemeRule[], customTokenColors: string[]): TokenTheme {
return this.createFromParsedTokenTheme(parseTokenTheme(source), customTokenColors);
}
public static createFromParsedTokenTheme(source: ParsedTokenThemeRule[]): TokenTheme {
return resolveParsedTokenThemeRules(source);
public static createFromParsedTokenTheme(source: ParsedTokenThemeRule[], customTokenColors: string[]): TokenTheme {
return resolveParsedTokenThemeRules(source, customTokenColors);
}
private readonly _colorMap: ColorMap;
@@ -391,6 +402,6 @@ export function generateTokensCSSForColorMap(colorMap: Color[]): string {
}
rules.push('.mtki { font-style: italic; }');
rules.push('.mtkb { font-weight: bold; }');
rules.push('.mtku { text-decoration: underline; }');
rules.push('.mtku { text-decoration: underline; text-underline-position: under; }');
return rules.join('\n');
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { ColorId, ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes';
import { Color } from 'vs/base/common/color';
@@ -33,15 +33,13 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry {
public register(language: string, support: ITokenizationSupport): IDisposable {
this._map[language] = support;
this.fire([language]);
return {
dispose: () => {
if (this._map[language] !== support) {
return;
}
delete this._map[language];
this.fire([language]);
return toDisposable(() => {
if (this._map[language] !== support) {
return;
}
};
delete this._map[language];
this.fire([language]);
});
}
public get(language: string): ITokenizationSupport {

View File

@@ -16,13 +16,13 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { IInplaceReplaceSupportResult, ILink, ISuggestResult, ISuggestion, TextEdit } from 'vs/editor/common/modes';
import { computeLinks } from 'vs/editor/common/modes/linkComputer';
import { computeLinks, ILinkComputerTarget } from 'vs/editor/common/modes/linkComputer';
import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
import { IWordAtPosition, EndOfLineSequence } from 'vs/editor/common/model';
import { globals } from 'vs/base/common/platform';
import { IIterator } from 'vs/base/common/iterator';
import { Iterator } from 'vs/base/common/iterator';
export interface IMirrorModel {
readonly uri: URI;
@@ -50,7 +50,7 @@ export interface IRawModelData {
/**
* @internal
*/
export interface ICommonModel {
export interface ICommonModel extends ILinkComputerTarget, IMirrorModel {
uri: URI;
version: number;
eol: string;
@@ -59,7 +59,7 @@ export interface ICommonModel {
getLinesContent(): string[];
getLineCount(): number;
getLineContent(lineNumber: number): string;
createWordIterator(wordDefinition: RegExp): IIterator<string>;
createWordIterator(wordDefinition: RegExp): Iterator<string>;
getWordUntilPosition(position: IPosition, wordDefinition: RegExp): IWordAtPosition;
getValueInRange(range: IRange): string;
getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range;
@@ -147,7 +147,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
};
}
public createWordIterator(wordDefinition: RegExp): IIterator<string> {
public createWordIterator(wordDefinition: RegExp): Iterator<string> {
let obj = {
done: false,
value: ''
@@ -156,7 +156,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
let lineText: string;
let wordRangesIdx = 0;
let wordRanges: IWordRange[] = [];
let next = () => {
let next = (): { done: boolean; value: string } => {
if (wordRangesIdx < wordRanges.length) {
obj.done = false;
@@ -303,7 +303,7 @@ export interface IForeignModuleFactory {
(ctx: IWorkerContext, createData: any): any;
}
declare var require;
declare var require: any;
/**
* @internal
@@ -332,6 +332,7 @@ export abstract class BaseEditorSimpleWorker {
let originalLines = original.getLinesContent();
let modifiedLines = modified.getLinesContent();
let diffComputer = new DiffComputer(originalLines, modifiedLines, {
shouldComputeCharChanges: true,
shouldPostProcessCharChanges: true,
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
shouldMakePrettyDiff: true
@@ -349,6 +350,7 @@ export abstract class BaseEditorSimpleWorker {
let originalLines = original.getLinesContent();
let modifiedLines = modified.getLinesContent();
let diffComputer = new DiffComputer(originalLines, modifiedLines, {
shouldComputeCharChanges: false,
shouldPostProcessCharChanges: false,
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
shouldMakePrettyDiff: true

View File

@@ -5,7 +5,7 @@
'use strict';
import { IntervalTimer, ShallowCancelThenPromise, wireCancellationToken } from 'vs/base/common/async';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
@@ -37,7 +37,7 @@ function canSyncModel(modelService: IModelService, resource: URI): boolean {
if (!model) {
return false;
}
if (model.isTooLargeForTokenization()) {
if (model.isTooLargeForSyncing()) {
return false;
}
return true;
@@ -265,7 +265,7 @@ class EditorModelManager extends Disposable {
if (!model) {
return;
}
if (model.isTooLargeForTokenization()) {
if (model.isTooLargeForSyncing()) {
return;
}
@@ -285,11 +285,9 @@ class EditorModelManager extends Disposable {
toDispose.push(model.onWillDispose(() => {
this._stopModelSync(modelUrl);
}));
toDispose.push({
dispose: () => {
this._proxy.acceptRemovedModel(modelUrl);
}
});
toDispose.push(toDisposable(() => {
this._proxy.acceptRemovedModel(modelUrl);
}));
this._syncedModels[modelUrl] = toDispose;
}

View File

@@ -13,6 +13,7 @@ import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService';
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { NULL_MODE_ID, NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import URI from 'vs/base/common/uri';
const hasOwnProperty = Object.prototype.hasOwnProperty;
@@ -23,7 +24,7 @@ export interface IResolvedLanguage {
aliases: string[];
extensions: string[];
filenames: string[];
configurationFiles: string[];
configurationFiles: URI[];
}
export class LanguagesRegistry {
@@ -191,7 +192,7 @@ export class LanguagesRegistry {
}
}
if (typeof lang.configuration === 'string') {
if (lang.configuration) {
resolvedLanguage.configurationFiles.push(lang.configuration);
}
}
@@ -227,7 +228,7 @@ export class LanguagesRegistry {
return this._lowercaseNameMap[languageNameLower].language;
}
public getConfigurationFiles(modeId: string): string[] {
public getConfigurationFiles(modeId: string): URI[] {
if (!hasOwnProperty.call(this._languages, modeId)) {
return [];
}

View File

@@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import URI from 'vs/base/common/uri';
export const IModeService = createDecorator<IModeService>('modeService');
@@ -19,18 +20,7 @@ export interface ILanguageExtensionPoint {
firstLine?: string;
aliases?: string[];
mimetypes?: string[];
configuration?: string;
}
export interface IValidLanguageExtensionPoint {
id: string;
extensions: string[];
filenames: string[];
filenamePatterns: string[];
firstLine: string;
aliases: string[];
mimetypes: string[];
configuration: string;
configuration?: URI;
}
export interface IModeService {
@@ -50,7 +40,7 @@ export interface IModeService {
getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string;
getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string;
getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier;
getConfigurationFiles(modeId: string): string[];
getConfigurationFiles(modeId: string): URI[];
// --- instantiation
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode;

View File

@@ -11,6 +11,7 @@ import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode';
import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry';
import { IModeService } from 'vs/editor/common/services/modeService';
import URI from 'vs/base/common/uri';
export class ModeServiceImpl implements IModeService {
public _serviceBrand: any;
@@ -87,7 +88,7 @@ export class ModeServiceImpl implements IModeService {
return this._registry.getLanguageIdentifier(modeId);
}
public getConfigurationFiles(modeId: string): string[] {
public getConfigurationFiles(modeId: string): URI[] {
return this._registry.getConfigurationFiles(modeId);
}

View File

@@ -11,7 +11,7 @@ import { MarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
@@ -127,10 +127,15 @@ class ModelMarkerHandler {
let color: ThemeColor;
let darkColor: ThemeColor;
let zIndex: number;
let inlineClassName: string;
switch (marker.severity) {
case MarkerSeverity.Hint:
className = ClassName.EditorHintDecoration;
if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) {
className = ClassName.EditorUnnecessaryDecoration;
} else {
className = ClassName.EditorHintDecoration;
}
zIndex = 0;
break;
case MarkerSeverity.Warning:
@@ -154,6 +159,12 @@ class ModelMarkerHandler {
break;
}
if (marker.tags) {
if (marker.tags.indexOf(MarkerTag.Unnecessary) !== -1) {
inlineClassName = ClassName.EditorUnnecessaryInlineDecoration;
}
}
let hoverMessage: MarkdownString = null;
let { message, source, relatedInformation } = marker;
@@ -174,8 +185,10 @@ class ModelMarkerHandler {
hoverMessage.appendMarkdown('\n');
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
hoverMessage.appendMarkdown(
`* [${basename(resource.path)}(${startLineNumber}, ${startColumn})](${resource.toString(false)}#${startLineNumber},${startColumn}): \`${message}\` \n`
`* [${basename(resource.path)}(${startLineNumber}, ${startColumn})](${resource.toString(false)}#${startLineNumber},${startColumn}): `
);
hoverMessage.appendText(`${message}`);
hoverMessage.appendMarkdown('\n');
}
hoverMessage.appendMarkdown('\n');
}
@@ -191,7 +204,8 @@ class ModelMarkerHandler {
darkColor,
position: OverviewRulerLane.Right
},
zIndex
zIndex,
inlineClassName,
};
}
}
@@ -260,6 +274,9 @@ export class ModelServiceImpl implements IModelService {
if (!isNaN(parsedTabSize)) {
tabSize = parsedTabSize;
}
if (tabSize < 1) {
tabSize = 1;
}
}
let insertSpaces = EDITOR_MODEL_DEFAULTS.insertSpaces;
@@ -418,7 +435,7 @@ export class ModelServiceImpl implements IModelService {
// Otherwise find a diff between the values and update model
model.pushStackElement();
model.setEOL(textBuffer.getEOL() === '\r\n' ? EndOfLineSequence.CRLF : EndOfLineSequence.LF);
model.pushEOL(textBuffer.getEOL() === '\r\n' ? EndOfLineSequence.CRLF : EndOfLineSequence.LF);
model.pushEditOperations(
[],
ModelServiceImpl._computeEdits(model, textBuffer),

View File

@@ -86,7 +86,7 @@ class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWork
let foreignProxy = {} as T;
for (let i = 0; i < foreignMethods.length; i++) {
foreignProxy[foreignMethods[i]] = createProxyMethod(foreignMethods[i], proxyMethodRequest);
(<any>foreignProxy)[foreignMethods[i]] = createProxyMethod(foreignMethods[i], proxyMethodRequest);
}
return foreignProxy;

View File

@@ -25,6 +25,10 @@ export enum Severity {
Error = 3,
}
export enum MarkerTag {
Unnecessary = 1,
}
export enum MarkerSeverity {
Hint = 1,
Info = 2,
@@ -246,6 +250,7 @@ export function createMonacoBaseAPI(): typeof monaco {
SelectionDirection: SelectionDirection,
Severity: Severity,
MarkerSeverity: MarkerSeverity,
MarkerTag: MarkerTag,
Promise: TPromise,
Uri: <any>URI,
Token: Token

View File

@@ -21,9 +21,9 @@ export const editorCursorBackground = registerColor('editorCursor.background', n
export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hc: '#e3e4e229' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.'));
export const editorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'));
export const editorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hc: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'));
export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#5A5A5A', light: '#2B91AF', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.'));
export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hc: Color.white }, nls.localize('editorLineNumbers', 'Color of editor line numbers.'));
const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: null, light: null, hc: null }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.'));
const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hc: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.'));
export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', { dark: deprecatedEditorActiveLineNumber, light: deprecatedEditorActiveLineNumber, hc: deprecatedEditorActiveLineNumber }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'));
export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hc: Color.white }, nls.localize('editorRuler', 'Color of the editor rulers.'));
@@ -49,6 +49,9 @@ export const editorInfoBorder = registerColor('editorInfo.border', { dark: null,
export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('hintForeground', 'Foreground color of hint squigglies in the editor.'));
export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint squigglies in the editor.'));
export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hc: Color.fromHex('#fff').transparent(0.8) }, nls.localize('unnecessaryCodeBorder', 'Border of unnecessary code in the editor.'));
export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary code in the editor.'));
const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6));
export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque to not hide underlying decorations.'), true);
export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.'));

View File

@@ -9,7 +9,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { ScrollEvent } from 'vs/base/common/scrollable';
import { IConfigurationChangedEvent } from 'vs/editor/common/config/editorOptions';
import * as errors from 'vs/base/common/errors';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { ScrollType } from 'vs/editor/common/editorCommon';
export const enum ViewEventType {
@@ -354,17 +354,15 @@ export class ViewEventEmitter extends Disposable {
public addEventListener(listener: (events: ViewEvent[]) => void): IDisposable {
this._listeners.push(listener);
return {
dispose: () => {
let listeners = this._listeners;
for (let i = 0, len = listeners.length; i < len; i++) {
if (listeners[i] === listener) {
listeners.splice(i, 1);
break;
}
return toDisposable(() => {
let listeners = this._listeners;
for (let i = 0, len = listeners.length; i < len; i++) {
if (listeners[i] === listener) {
listeners.splice(i, 1);
break;
}
}
};
});
}
}

View File

@@ -64,8 +64,8 @@ export class LinesLayout {
* @param heightInPx The height of the whitespace, in pixels.
* @return An id that can be used later to mutate or delete the whitespace
*/
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number): number {
return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx);
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number {
return this._whitespaces.insertWhitespace(afterLineNumber, ordinal, heightInPx, minWidth);
}
/**
@@ -155,6 +155,10 @@ export class LinesLayout {
return this._whitespaces.getCount() > 0;
}
public getWhitespaceMinWidth(): number {
return this._whitespaces.getMinWidth();
}
/**
* Check if `verticalOffset` is below all lines.
*/

View File

@@ -18,8 +18,6 @@ const SMOOTH_SCROLLING_TIME = 125;
export class ViewLayout extends Disposable implements IViewLayout {
static LINES_HORIZONTAL_EXTRA_PX = 30;
private readonly _configuration: editorCommon.IConfiguration;
private readonly _linesLayout: LinesLayout;
@@ -143,7 +141,9 @@ export class ViewLayout extends Disposable implements IViewLayout {
private _computeScrollWidth(maxLineWidth: number, viewportWidth: number): number {
let isViewportWrapping = this._configuration.editor.wrappingInfo.isViewportWrapping;
if (!isViewportWrapping) {
return Math.max(maxLineWidth + ViewLayout.LINES_HORIZONTAL_EXTRA_PX, viewportWidth);
const extraHorizontalSpace = this._configuration.editor.viewInfo.scrollBeyondLastColumn * this._configuration.editor.fontInfo.typicalHalfwidthCharacterWidth;
const whitespaceMinWidth = this._linesLayout.getWhitespaceMinWidth();
return Math.max(maxLineWidth + extraHorizontalSpace, viewportWidth, whitespaceMinWidth);
}
return Math.max(maxLineWidth, viewportWidth);
}
@@ -174,8 +174,8 @@ export class ViewLayout extends Disposable implements IViewLayout {
// ---- IVerticalLayoutProvider
public addWhitespace(afterLineNumber: number, ordinal: number, height: number): number {
return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height);
public addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number {
return this._linesLayout.insertWhitespace(afterLineNumber, ordinal, height, minWidth);
}
public changeWhitespace(id: number, newAfterLineNumber: number, newHeight: number): boolean {
return this._linesLayout.changeWhitespace(id, newAfterLineNumber, newHeight);

View File

@@ -36,6 +36,7 @@ export class RenderLineInput {
public readonly useMonospaceOptimizations: boolean;
public readonly lineContent: string;
public readonly continuesWithWrappedLine: boolean;
public readonly isBasicASCII: boolean;
public readonly containsRTL: boolean;
public readonly fauxIndentLength: number;
@@ -51,6 +52,7 @@ export class RenderLineInput {
constructor(
useMonospaceOptimizations: boolean,
lineContent: string,
continuesWithWrappedLine: boolean,
isBasicASCII: boolean,
containsRTL: boolean,
fauxIndentLength: number,
@@ -65,6 +67,7 @@ export class RenderLineInput {
) {
this.useMonospaceOptimizations = useMonospaceOptimizations;
this.lineContent = lineContent;
this.continuesWithWrappedLine = continuesWithWrappedLine;
this.isBasicASCII = isBasicASCII;
this.containsRTL = containsRTL;
this.fauxIndentLength = fauxIndentLength;
@@ -88,6 +91,7 @@ export class RenderLineInput {
return (
this.useMonospaceOptimizations === other.useMonospaceOptimizations
&& this.lineContent === other.lineContent
&& this.continuesWithWrappedLine === other.continuesWithWrappedLine
&& this.isBasicASCII === other.isBasicASCII
&& this.containsRTL === other.containsRTL
&& this.fauxIndentLength === other.fauxIndentLength
@@ -331,7 +335,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
let tokens = transformAndRemoveOverflowing(input.lineTokens, input.fauxIndentLength, len);
if (input.renderWhitespace === RenderWhitespace.All || input.renderWhitespace === RenderWhitespace.Boundary) {
tokens = _applyRenderWhitespace(lineContent, len, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.renderWhitespace === RenderWhitespace.Boundary);
tokens = _applyRenderWhitespace(lineContent, len, input.continuesWithWrappedLine, tokens, input.fauxIndentLength, input.tabSize, useMonospaceOptimizations, input.renderWhitespace === RenderWhitespace.Boundary);
}
let containsForeignElements = ForeignElementType.None;
if (input.lineDecorations.length > 0) {
@@ -437,7 +441,7 @@ function splitLargeTokens(lineContent: string, tokens: LinePart[]): LinePart[] {
* Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (&rarr; or &middot;) do not have the same width as &nbsp;.
* The rendering phase will generate `style="width:..."` for these tokens.
*/
function _applyRenderWhitespace(lineContent: string, len: number, tokens: LinePart[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, onlyBoundary: boolean): LinePart[] {
function _applyRenderWhitespace(lineContent: string, len: number, continuesWithWrappedLine: boolean, tokens: LinePart[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, onlyBoundary: boolean): LinePart[] {
let result: LinePart[] = [], resultLen = 0;
let tokenIndex = 0;
@@ -527,14 +531,23 @@ function _applyRenderWhitespace(lineContent: string, len: number, tokens: LinePa
}
}
let generateWhitespace = false;
if (wasInWhitespace) {
// was in whitespace token
result[resultLen++] = new LinePart(len, 'vs-whitespace');
} else {
// was in regular token
result[resultLen++] = new LinePart(len, tokenType);
if (continuesWithWrappedLine && onlyBoundary) {
let lastCharCode = (len > 0 ? lineContent.charCodeAt(len - 1) : CharCode.Null);
let prevCharCode = (len > 1 ? lineContent.charCodeAt(len - 2) : CharCode.Null);
let isSingleTrailingSpace = (lastCharCode === CharCode.Space && (prevCharCode !== CharCode.Space && prevCharCode !== CharCode.Tab));
if (!isSingleTrailingSpace) {
generateWhitespace = true;
}
} else {
generateWhitespace = true;
}
}
result[resultLen++] = new LinePart(len, generateWhitespace ? 'vs-whitespace' : tokenType);
return result;
}
@@ -675,7 +688,11 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
tabsCharDelta += insertSpacesCount - 1;
charOffsetInPart += insertSpacesCount - 1;
if (insertSpacesCount > 0) {
sb.write1(0x2192); // &rarr;
if (insertSpacesCount > 1) {
sb.write1(0x2192); // RIGHTWARDS ARROW
} else {
sb.write1(0xffeb); // HALFWIDTH RIGHTWARDS ARROW
}
insertSpacesCount--;
}
while (insertSpacesCount > 0) {

View File

@@ -21,6 +21,11 @@ export class WhitespaceComputer {
*/
private _heights: number[];
/**
* minWidths[i] is the min width in pixels for whitespace at index i
*/
private _minWidths: number[];
/**
* afterLineNumbers[i] is the line number whitespace at index i is after
*/
@@ -58,8 +63,11 @@ export class WhitespaceComputer {
*/
private _lastWhitespaceId: number;
private _minWidth: number;
constructor() {
this._heights = [];
this._minWidths = [];
this._ids = [];
this._afterLineNumbers = [];
this._ordinals = [];
@@ -67,6 +75,7 @@ export class WhitespaceComputer {
this._prefixSumValidIndex = -1;
this._whitespaceId2Index = {};
this._lastWhitespaceId = 0;
this._minWidth = -1; /* marker for not being computed */
}
/**
@@ -105,25 +114,29 @@ export class WhitespaceComputer {
* @param heightInPx The height of the whitespace, in pixels.
* @return An id that can be used later to mutate or delete the whitespace
*/
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number): number {
public insertWhitespace(afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): number {
afterLineNumber = afterLineNumber | 0;
ordinal = ordinal | 0;
heightInPx = heightInPx | 0;
minWidth = minWidth | 0;
let id = (++this._lastWhitespaceId);
let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, afterLineNumber, this._ordinals, ordinal);
this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx);
this._insertWhitespaceAtIndex(id, insertionIndex, afterLineNumber, ordinal, heightInPx, minWidth);
this._minWidth = -1; /* marker for not being computed */
return id;
}
private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number): void {
private _insertWhitespaceAtIndex(id: number, insertIndex: number, afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): void {
id = id | 0;
insertIndex = insertIndex | 0;
afterLineNumber = afterLineNumber | 0;
ordinal = ordinal | 0;
heightInPx = heightInPx | 0;
minWidth = minWidth | 0;
this._heights.splice(insertIndex, 0, heightInPx);
this._minWidths.splice(insertIndex, 0, minWidth);
this._ids.splice(insertIndex, 0, id);
this._afterLineNumbers.splice(insertIndex, 0, afterLineNumber);
this._ordinals.splice(insertIndex, 0, ordinal);
@@ -202,12 +215,15 @@ export class WhitespaceComputer {
// Record old height
let heightInPx = this._heights[index];
// Record old min width
let minWidth = this._minWidths[index];
// Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace
this.removeWhitespace(id);
// And add it again
let insertionIndex = WhitespaceComputer.findInsertionIndex(this._afterLineNumbers, newAfterLineNumber, this._ordinals, ordinal);
this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx);
this._insertWhitespaceAtIndex(id, insertionIndex, newAfterLineNumber, ordinal, heightInPx, minWidth);
return true;
}
@@ -230,6 +246,7 @@ export class WhitespaceComputer {
let index = this._whitespaceId2Index[sid];
delete this._whitespaceId2Index[sid];
this._removeWhitespaceAtIndex(index);
this._minWidth = -1; /* marker for not being computed */
return true;
}
@@ -240,6 +257,7 @@ export class WhitespaceComputer {
removeIndex = removeIndex | 0;
this._heights.splice(removeIndex, 1);
this._minWidths.splice(removeIndex, 1);
this._ids.splice(removeIndex, 1);
this._afterLineNumbers.splice(removeIndex, 1);
this._ordinals.splice(removeIndex, 1);
@@ -410,6 +428,20 @@ export class WhitespaceComputer {
return this._heights.length;
}
/**
* The maximum min width for all whitespaces.
*/
public getMinWidth(): number {
if (this._minWidth === -1) {
let minWidth = 0;
for (let i = 0, len = this._minWidths.length; i < len; i++) {
minWidth = Math.max(minWidth, this._minWidths[i]);
}
this._minWidth = minWidth;
}
return this._minWidth;
}
/**
* Get the `afterLineNumber` for whitespace at index `index`.
*

View File

@@ -92,14 +92,24 @@ export class CharacterHardWrappingLineMapperFactory implements ILineMapperFactor
if (hardWrappingIndent !== WrappingIndent.None) {
firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineText);
if (firstNonWhitespaceIndex !== -1) {
// Track existing indent
wrappedTextIndent = lineText.substring(0, firstNonWhitespaceIndex);
for (let i = 0; i < firstNonWhitespaceIndex; i++) {
wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, lineText.charCodeAt(i) === CharCode.Tab, 1);
}
// Increase indent of continuation lines, if desired
let numberOfAdditionalTabs = 0;
if (hardWrappingIndent === WrappingIndent.Indent) {
numberOfAdditionalTabs = 1;
} else if (hardWrappingIndent === WrappingIndent.DeepIndent) {
numberOfAdditionalTabs = 2;
}
for (let i = 0; i < numberOfAdditionalTabs; i++) {
wrappedTextIndent += '\t';
wrappedTextIndentVisibleColumn = CharacterHardWrappingLineMapperFactory.nextVisibleColumn(wrappedTextIndentVisibleColumn, tabSize, true, 1);
}
// Force sticking to beginning of line if no character would fit except for the indentation
if (wrappedTextIndentVisibleColumn + columnsForFullWidthChar > breakingColumn) {
wrappedTextIndent = '';

View File

@@ -143,6 +143,12 @@ export class CoordinatesConverter implements ICoordinatesConverter {
}
const enum IndentGuideRepeatOption {
BlockNone = 0,
BlockSubsequent = 1,
BlockAll = 2
}
export class SplitLinesCollection implements IViewModelLinesCollection {
private model: ITextModel;
@@ -535,6 +541,7 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
let result: number[] = [];
let resultRepeatCount: number[] = [];
let resultRepeatOption: IndentGuideRepeatOption[] = [];
const modelStartLineIndex = modelStart.lineNumber - 1;
const modelEndLineIndex = modelEnd.lineNumber - 1;
@@ -542,17 +549,16 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
for (let modelLineIndex = modelStartLineIndex; modelLineIndex <= modelEndLineIndex; modelLineIndex++) {
const line = this.lines[modelLineIndex];
if (line.isVisible()) {
let count = 0;
if (modelLineIndex === modelStartLineIndex) {
let viewLineStartIndex = line.getViewLineNumberOfModelPosition(0, modelStart.column);
let viewLineEndIndex = line.getViewLineNumberOfModelPosition(0, this.model.getLineMaxColumn(modelLineIndex + 1));
count = viewLineEndIndex - viewLineStartIndex + 1;
} else {
let viewLineStartIndex = line.getViewLineNumberOfModelPosition(0, 1);
let viewLineEndIndex = line.getViewLineNumberOfModelPosition(0, this.model.getLineMaxColumn(modelLineIndex + 1));
count = viewLineEndIndex - viewLineStartIndex + 1;
let viewLineStartIndex = line.getViewLineNumberOfModelPosition(0, modelLineIndex === modelStartLineIndex ? modelStart.column : 1);
let viewLineEndIndex = line.getViewLineNumberOfModelPosition(0, this.model.getLineMaxColumn(modelLineIndex + 1));
let count = viewLineEndIndex - viewLineStartIndex + 1;
let option = IndentGuideRepeatOption.BlockNone;
if (count > 1 && line.getViewLineMinColumn(this.model, modelLineIndex + 1, viewLineEndIndex) === 1) {
// wrapped lines should block indent guides
option = (viewLineStartIndex === 0 ? IndentGuideRepeatOption.BlockSubsequent : IndentGuideRepeatOption.BlockAll);
}
resultRepeatCount.push(count);
resultRepeatOption.push(option);
// merge into previous request
if (reqStart === null) {
reqStart = new Position(modelLineIndex + 1, 0);
@@ -577,7 +583,19 @@ export class SplitLinesCollection implements IViewModelLinesCollection {
for (let i = 0, len = result.length; i < len; i++) {
let value = result[i];
let count = Math.min(viewLineCount - currIndex, resultRepeatCount[i]);
let option = resultRepeatOption[i];
let blockAtIndex: number;
if (option === IndentGuideRepeatOption.BlockAll) {
blockAtIndex = 0;
} else if (option === IndentGuideRepeatOption.BlockSubsequent) {
blockAtIndex = 1;
} else {
blockAtIndex = count;
}
for (let j = 0; j < count; j++) {
if (j === blockAtIndex) {
value = 0;
}
viewIndents[currIndex++] = value;
}
}
@@ -866,6 +884,7 @@ class VisibleIdentitySplitLine implements ISplitLine {
let lineContent = lineTokens.getLineContent();
return new ViewLineData(
lineContent,
false,
1,
lineContent.length + 1,
lineTokens.inflate()
@@ -1069,6 +1088,8 @@ export class SplitLine implements ISplitLine {
let minColumn = (outputLineIndex > 0 ? this.wrappedIndentLength + 1 : 1);
let maxColumn = lineContent.length + 1;
let continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount());
let deltaStartIndex = 0;
if (outputLineIndex > 0) {
deltaStartIndex = this.wrappedIndentLength;
@@ -1077,6 +1098,7 @@ export class SplitLine implements ISplitLine {
return new ViewLineData(
lineContent,
continuesWithWrappedLine,
minColumn,
maxColumn,
lineTokens.sliceAndInflate(startOffset, endOffset, deltaStartIndex)
@@ -1300,6 +1322,7 @@ export class IdentityLinesCollection implements IViewModelLinesCollection {
let lineContent = lineTokens.getLineContent();
return new ViewLineData(
lineContent,
false,
1,
lineContent.length + 1,
lineTokens.inflate()

View File

@@ -75,7 +75,7 @@ export interface IViewLayout {
* Reserve rendering space.
* @return an identifier that can be later used to remove or change the whitespace.
*/
addWhitespace(afterLineNumber: number, ordinal: number, height: number): number;
addWhitespace(afterLineNumber: number, ordinal: number, height: number, minWidth: number): number;
/**
* Change the properties of a whitespace.
*/
@@ -172,6 +172,10 @@ export class ViewLineData {
* The content at this view line.
*/
public readonly content: string;
/**
* Does this line continue with a wrapped line?
*/
public readonly continuesWithWrappedLine: boolean;
/**
* The minimum allowed column at this view line.
*/
@@ -187,11 +191,13 @@ export class ViewLineData {
constructor(
content: string,
continuesWithWrappedLine: boolean,
minColumn: number,
maxColumn: number,
tokens: IViewLineTokens
) {
this.content = content;
this.continuesWithWrappedLine = continuesWithWrappedLine;
this.minColumn = minColumn;
this.maxColumn = maxColumn;
this.tokens = tokens;
@@ -211,6 +217,10 @@ export class ViewLineRenderingData {
* The content at this view line.
*/
public readonly content: string;
/**
* Does this line continue with a wrapped line?
*/
public readonly continuesWithWrappedLine: boolean;
/**
* Describes if `content` contains RTL characters.
*/
@@ -236,6 +246,7 @@ export class ViewLineRenderingData {
minColumn: number,
maxColumn: number,
content: string,
continuesWithWrappedLine: boolean,
mightContainRTL: boolean,
mightContainNonBasicASCII: boolean,
tokens: IViewLineTokens,
@@ -245,6 +256,7 @@ export class ViewLineRenderingData {
this.minColumn = minColumn;
this.maxColumn = maxColumn;
this.content = content;
this.continuesWithWrappedLine = continuesWithWrappedLine;
this.isBasicASCII = ViewLineRenderingData.isBasicASCII(content, mightContainNonBasicASCII);
this.containsRTL = ViewLineRenderingData.containsRTL(content, this.isBasicASCII, mightContainRTL);

View File

@@ -322,6 +322,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
eventsCollector.emit(new viewEvents.ViewDecorationsChangedEvent());
this.decorations.onLineMappingChanged();
this.viewLayout.onFlushed(this.getLineCount());
this.viewLayout.onHeightMaybeChanged();
}
} finally {
this._endEmit();
@@ -508,6 +509,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
lineData.minColumn,
lineData.maxColumn,
lineData.content,
lineData.continuesWithWrappedLine,
mightContainRTL,
mightContainNonBasicASCII,
lineData.tokens,