mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 (#7880)
* Merge from vscode c58aaab8a1cc22a7139b761166a0d4f37d41e998 * fix pipelines * fix strict-null-checks * add missing files
This commit is contained in:
@@ -36,6 +36,27 @@ export class ReplaceCommand implements ICommand {
|
||||
}
|
||||
}
|
||||
|
||||
export class ReplaceCommandThatSelectsText implements ICommand {
|
||||
|
||||
private readonly _range: Range;
|
||||
private readonly _text: string;
|
||||
|
||||
constructor(range: Range, text: string) {
|
||||
this._range = range;
|
||||
this._text = text;
|
||||
}
|
||||
|
||||
public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void {
|
||||
builder.addTrackedEditOperation(this._range, this._text);
|
||||
}
|
||||
|
||||
public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection {
|
||||
const inverseEditOperations = helper.getInverseEditOperations();
|
||||
const srcRange = inverseEditOperations[0].range;
|
||||
return new Selection(srcRange.startLineNumber, srcRange.startColumn, srcRange.endLineNumber, srcRange.endColumn);
|
||||
}
|
||||
}
|
||||
|
||||
export class ReplaceCommandWithoutChangingPosition implements ICommand {
|
||||
|
||||
private readonly _range: Range;
|
||||
|
||||
@@ -207,6 +207,17 @@ function migrateOptions(options: IEditorOptions): void {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
|
||||
const parameterHints = options.parameterHints;
|
||||
if (<any>parameterHints === true) {
|
||||
options.parameterHints = {
|
||||
enabled: true
|
||||
};
|
||||
} else if (<any>parameterHints === false) {
|
||||
options.parameterHints = {
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function deepCloneAndMigrateOptions(_options: IEditorOptions): IEditorOptions {
|
||||
@@ -463,6 +474,11 @@ const editorConfiguration: IConfigurationNode = {
|
||||
default: 750,
|
||||
description: nls.localize('codeActionsOnSaveTimeout', "Timeout in milliseconds after which the code actions that are run on save are cancelled.")
|
||||
},
|
||||
'diffEditor.maxComputationTime': {
|
||||
type: 'number',
|
||||
default: 5000,
|
||||
description: nls.localize('maxComputationTime', "Timeout in milliseconds after which diff computation is cancelled. Use 0 for no timeout.")
|
||||
},
|
||||
'diffEditor.renderSideBySide': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
|
||||
@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { FontInfo } from 'vs/editor/common/config/fontInfo';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/model/wordHelper';
|
||||
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
@@ -178,7 +178,7 @@ export interface IEditorOptions {
|
||||
* Enable font ligatures.
|
||||
* Defaults to false.
|
||||
*/
|
||||
fontLigatures?: boolean;
|
||||
fontLigatures?: boolean | string;
|
||||
/**
|
||||
* Disable the use of `will-change` for the editor margin and lines layers.
|
||||
* The usage of `will-change` acts as a hint for browsers to create an extra layer.
|
||||
@@ -538,6 +538,11 @@ export interface IDiffEditorOptions extends IEditorOptions {
|
||||
* Defaults to true.
|
||||
*/
|
||||
renderSideBySide?: boolean;
|
||||
/**
|
||||
* Timeout in milliseconds after which diff computation is cancelled.
|
||||
* Defaults to 5000.
|
||||
*/
|
||||
maxComputationTime?: number;
|
||||
/**
|
||||
* Compute the diff by ignoring leading/trailing whitespace
|
||||
* Defaults to true.
|
||||
@@ -647,6 +652,9 @@ export interface IEditorOption<K1 extends EditorOption, V> {
|
||||
type PossibleKeyName0<V> = { [K in keyof IEditorOptions]: IEditorOptions[K] extends V | undefined ? K : never }[keyof IEditorOptions];
|
||||
type PossibleKeyName<V> = NonNullable<PossibleKeyName0<V>>;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
abstract class BaseEditorOption<K1 extends EditorOption, V> implements IEditorOption<K1, V> {
|
||||
|
||||
public readonly id: K1;
|
||||
@@ -1050,7 +1058,7 @@ function _cursorStyleFromString(cursorStyle: 'line' | 'block' | 'underline' | 'l
|
||||
class EditorClassName extends ComputedEditorOption<EditorOption.editorClassName, string> {
|
||||
|
||||
constructor() {
|
||||
super(EditorOption.editorClassName, [EditorOption.mouseStyle, EditorOption.fontLigatures, EditorOption.extraEditorClassName]);
|
||||
super(EditorOption.editorClassName, [EditorOption.mouseStyle, EditorOption.extraEditorClassName]);
|
||||
}
|
||||
|
||||
public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: string): string {
|
||||
@@ -1061,9 +1069,6 @@ class EditorClassName extends ComputedEditorOption<EditorOption.editorClassName,
|
||||
if (env.extraEditorClassName) {
|
||||
className += ' ' + env.extraEditorClassName;
|
||||
}
|
||||
if (options.get(EditorOption.fontLigatures)) {
|
||||
className += ' enable-ligatures';
|
||||
}
|
||||
if (options.get(EditorOption.mouseStyle) === 'default') {
|
||||
className += ' mouse-default';
|
||||
} else if (options.get(EditorOption.mouseStyle) === 'copy') {
|
||||
@@ -1173,6 +1178,57 @@ class EditorFind extends BaseEditorOption<EditorOption.find, EditorFindOptions>
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region fontLigatures
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class EditorFontLigatures extends BaseEditorOption<EditorOption.fontLigatures, string> {
|
||||
|
||||
public static OFF = '"liga" off, "calt" off';
|
||||
public static ON = '"liga" on, "calt" on';
|
||||
|
||||
constructor() {
|
||||
super(
|
||||
EditorOption.fontLigatures, 'fontLigatures', EditorFontLigatures.OFF,
|
||||
{
|
||||
anyOf: [
|
||||
{
|
||||
type: 'boolean',
|
||||
description: nls.localize('fontLigatures', "Enables/Disables font ligatures."),
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
description: nls.localize('fontFeatureSettings', "Explicit font-feature-settings.")
|
||||
}
|
||||
],
|
||||
default: false
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public validate(input: any): string {
|
||||
if (typeof input === 'undefined') {
|
||||
return this.defaultValue;
|
||||
}
|
||||
if (typeof input === 'string') {
|
||||
if (input === 'false') {
|
||||
return EditorFontLigatures.OFF;
|
||||
}
|
||||
if (input === 'true') {
|
||||
return EditorFontLigatures.ON;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
if (Boolean(input)) {
|
||||
return EditorFontLigatures.ON;
|
||||
}
|
||||
return EditorFontLigatures.OFF;
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region fontInfo
|
||||
|
||||
class EditorFontInfo extends ComputedEditorOption<EditorOption.fontInfo, FontInfo> {
|
||||
@@ -1364,10 +1420,8 @@ export interface OverviewRulerPosition {
|
||||
|
||||
export const enum RenderMinimap {
|
||||
None = 0,
|
||||
Small = 1,
|
||||
Large = 2,
|
||||
SmallBlocks = 3,
|
||||
LargeBlocks = 4,
|
||||
Text = 1,
|
||||
Blocks = 2,
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1523,6 +1577,7 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
const minimapEnabled = minimap.enabled;
|
||||
const minimapSide = minimap.side;
|
||||
const minimapRenderCharacters = minimap.renderCharacters;
|
||||
const minimapScale = (pixelRatio >= 2 ? Math.round(minimap.scale * 2) : minimap.scale);
|
||||
const minimapMaxColumn = minimap.maxColumn | 0;
|
||||
|
||||
const scrollbar = options.get(EditorOption.scrollbar);
|
||||
@@ -1573,14 +1628,10 @@ export class EditorLayoutInfoComputer extends ComputedEditorOption<EditorOption.
|
||||
renderMinimap = RenderMinimap.None;
|
||||
contentWidth = remainingWidth;
|
||||
} else {
|
||||
let minimapCharWidth: number;
|
||||
if (pixelRatio >= 2) {
|
||||
renderMinimap = minimapRenderCharacters ? RenderMinimap.Large : RenderMinimap.LargeBlocks;
|
||||
minimapCharWidth = 2 / pixelRatio;
|
||||
} else {
|
||||
renderMinimap = minimapRenderCharacters ? RenderMinimap.Small : RenderMinimap.SmallBlocks;
|
||||
minimapCharWidth = 1 / pixelRatio;
|
||||
}
|
||||
// The minimapScale is also the pixel width of each character. Adjust
|
||||
// for the pixel ratio of the screen.
|
||||
const minimapCharWidth = minimapScale / pixelRatio;
|
||||
renderMinimap = minimapRenderCharacters ? RenderMinimap.Text : RenderMinimap.Blocks;
|
||||
|
||||
// Given:
|
||||
// (leaving 2px for the cursor to have space after the last character)
|
||||
@@ -1756,6 +1807,11 @@ export interface IEditorMinimapOptions {
|
||||
* Defaults to 120.
|
||||
*/
|
||||
maxColumn?: number;
|
||||
|
||||
/**
|
||||
* Relative size of the font in the minimap. Defaults to 1.
|
||||
*/
|
||||
scale?: number;
|
||||
}
|
||||
|
||||
export type EditorMinimapOptions = Readonly<Required<IEditorMinimapOptions>>;
|
||||
@@ -1769,6 +1825,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
showSlider: 'mouseover',
|
||||
renderCharacters: true,
|
||||
maxColumn: 120,
|
||||
scale: 1,
|
||||
};
|
||||
super(
|
||||
EditorOption.minimap, 'minimap', defaults,
|
||||
@@ -1788,7 +1845,14 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
type: 'string',
|
||||
enum: ['always', 'mouseover'],
|
||||
default: defaults.showSlider,
|
||||
description: nls.localize('minimap.showSlider', "Controls whether the minimap slider is automatically hidden.")
|
||||
description: nls.localize('minimap.showSlider', "Controls when the minimap slider is shown.")
|
||||
},
|
||||
'editor.minimap.scale': {
|
||||
type: 'number',
|
||||
default: defaults.scale,
|
||||
minimum: 1,
|
||||
maximum: 3,
|
||||
description: nls.localize('minimap.scale', "Scale of content drawn in the minimap.")
|
||||
},
|
||||
'editor.minimap.renderCharacters': {
|
||||
type: 'boolean',
|
||||
@@ -1814,6 +1878,7 @@ class EditorMinimap extends BaseEditorOption<EditorOption.minimap, EditorMinimap
|
||||
side: EditorStringEnumOption.stringSet<'right' | 'left'>(input.side, this.defaultValue.side, ['right', 'left']),
|
||||
showSlider: EditorStringEnumOption.stringSet<'always' | 'mouseover'>(input.showSlider, this.defaultValue.showSlider, ['always', 'mouseover']),
|
||||
renderCharacters: EditorBooleanOption.boolean(input.renderCharacters, this.defaultValue.renderCharacters),
|
||||
scale: EditorIntOption.clampedInt(input.scale, 1, 1, 3),
|
||||
maxColumn: EditorIntOption.clampedInt(input.maxColumn, this.defaultValue.maxColumn, 1, 10000),
|
||||
};
|
||||
}
|
||||
@@ -1960,6 +2025,7 @@ class EditorQuickSuggestions extends BaseEditorOption<EditorOption.quickSuggesti
|
||||
description: nls.localize('quickSuggestions', "Controls whether suggestions should automatically show up while typing.")
|
||||
}
|
||||
);
|
||||
this.defaultValue = defaults;
|
||||
}
|
||||
|
||||
public validate(_input: any): ValidQuickSuggestionsOptions {
|
||||
@@ -2226,6 +2292,10 @@ class EditorScrollbar extends BaseEditorOption<EditorOption.scrollbar, InternalE
|
||||
* Configuration options for editor suggest widget
|
||||
*/
|
||||
export interface ISuggestOptions {
|
||||
/**
|
||||
* Overwrite word ends on accept. Default to false.
|
||||
*/
|
||||
overwriteOnAccept?: boolean;
|
||||
/**
|
||||
* Enable graceful matching. Defaults to true.
|
||||
*/
|
||||
@@ -2262,6 +2332,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
|
||||
constructor() {
|
||||
const defaults: InternalSuggestOptions = {
|
||||
overwriteOnAccept: false,
|
||||
filterGraceful: true,
|
||||
snippetsPreventQuickSuggestions: true,
|
||||
localityBonus: false,
|
||||
@@ -2273,6 +2344,11 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
super(
|
||||
EditorOption.suggest, 'suggest', defaults,
|
||||
{
|
||||
'editor.suggest.overwriteOnAccept': {
|
||||
type: 'boolean',
|
||||
default: defaults.overwriteOnAccept,
|
||||
description: nls.localize('suggest.overwriteOnAccept', "Controls whether words are overwritten when accepting completions.")
|
||||
},
|
||||
'editor.suggest.filterGraceful': {
|
||||
type: 'boolean',
|
||||
default: defaults.filterGraceful,
|
||||
@@ -2305,142 +2381,135 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
maximum: 15,
|
||||
description: nls.localize('suggest.maxVisibleSuggestions', "Controls how many suggestions IntelliSense will show before showing a scrollbar (maximum 15).")
|
||||
},
|
||||
'editor.suggest.filteredTypes': {
|
||||
type: 'object',
|
||||
default: { keyword: true, snippet: true },
|
||||
markdownDescription: nls.localize('suggest.filtered', "Controls whether some suggestion types should be filtered from IntelliSense. A list of suggestion types can be found here: https://code.visualstudio.com/docs/editor/intellisense#_types-of-completions."),
|
||||
properties: {
|
||||
method: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.method', "When set to `false` IntelliSense never shows `method` suggestions.")
|
||||
},
|
||||
function: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.function', "When set to `false` IntelliSense never shows `function` suggestions.")
|
||||
},
|
||||
constructor: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.constructor', "When set to `false` IntelliSense never shows `constructor` suggestions.")
|
||||
},
|
||||
field: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.field', "When set to `false` IntelliSense never shows `field` suggestions.")
|
||||
},
|
||||
variable: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.variable', "When set to `false` IntelliSense never shows `variable` suggestions.")
|
||||
},
|
||||
class: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.class', "When set to `false` IntelliSense never shows `class` suggestions.")
|
||||
},
|
||||
struct: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.struct', "When set to `false` IntelliSense never shows `struct` suggestions.")
|
||||
},
|
||||
interface: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.interface', "When set to `false` IntelliSense never shows `interface` suggestions.")
|
||||
},
|
||||
module: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.module', "When set to `false` IntelliSense never shows `module` suggestions.")
|
||||
},
|
||||
property: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.property', "When set to `false` IntelliSense never shows `property` suggestions.")
|
||||
},
|
||||
event: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.event', "When set to `false` IntelliSense never shows `event` suggestions.")
|
||||
},
|
||||
operator: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.operator', "When set to `false` IntelliSense never shows `operator` suggestions.")
|
||||
},
|
||||
unit: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.unit', "When set to `false` IntelliSense never shows `unit` suggestions.")
|
||||
},
|
||||
value: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.value', "When set to `false` IntelliSense never shows `value` suggestions.")
|
||||
},
|
||||
constant: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.constant', "When set to `false` IntelliSense never shows `constant` suggestions.")
|
||||
},
|
||||
enum: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.enum', "When set to `false` IntelliSense never shows `enum` suggestions.")
|
||||
},
|
||||
enumMember: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.enumMember', "When set to `false` IntelliSense never shows `enumMember` suggestions.")
|
||||
},
|
||||
keyword: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.keyword', "When set to `false` IntelliSense never shows `keyword` suggestions.")
|
||||
},
|
||||
text: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.text', "When set to `false` IntelliSense never shows `text` suggestions.")
|
||||
},
|
||||
color: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.color', "When set to `false` IntelliSense never shows `color` suggestions.")
|
||||
},
|
||||
file: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.file', "When set to `false` IntelliSense never shows `file` suggestions.")
|
||||
},
|
||||
reference: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.reference', "When set to `false` IntelliSense never shows `reference` suggestions.")
|
||||
},
|
||||
customcolor: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.customcolor', "When set to `false` IntelliSense never shows `customcolor` suggestions.")
|
||||
},
|
||||
folder: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.folder', "When set to `false` IntelliSense never shows `folder` suggestions.")
|
||||
},
|
||||
typeParameter: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.typeParameter', "When set to `false` IntelliSense never shows `typeParameter` suggestions.")
|
||||
},
|
||||
snippet: {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.snippet', "When set to `false` IntelliSense never shows `snippet` suggestions.")
|
||||
},
|
||||
}
|
||||
'editor.suggest.filteredTypes.method': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.method', "When set to `false` IntelliSense never shows `method` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.function': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.function', "When set to `false` IntelliSense never shows `function` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.constructor': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.constructor', "When set to `false` IntelliSense never shows `constructor` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.field': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.field', "When set to `false` IntelliSense never shows `field` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.variable': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.variable', "When set to `false` IntelliSense never shows `variable` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.class': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.class', "When set to `false` IntelliSense never shows `class` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.struct': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.struct', "When set to `false` IntelliSense never shows `struct` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.interface': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.interface', "When set to `false` IntelliSense never shows `interface` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.module': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.module', "When set to `false` IntelliSense never shows `module` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.property': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.property', "When set to `false` IntelliSense never shows `property` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.event': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.event', "When set to `false` IntelliSense never shows `event` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.operator': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.operator', "When set to `false` IntelliSense never shows `operator` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.unit': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.unit', "When set to `false` IntelliSense never shows `unit` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.value': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.value', "When set to `false` IntelliSense never shows `value` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.constant': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.constant', "When set to `false` IntelliSense never shows `constant` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.enum': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.enum', "When set to `false` IntelliSense never shows `enum` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.enumMember': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.enumMember', "When set to `false` IntelliSense never shows `enumMember` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.keyword': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.keyword', "When set to `false` IntelliSense never shows `keyword` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.text': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.text', "When set to `false` IntelliSense never shows `text` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.color': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.color', "When set to `false` IntelliSense never shows `color` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.file': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.file', "When set to `false` IntelliSense never shows `file` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.reference': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.reference', "When set to `false` IntelliSense never shows `reference` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.customcolor': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.customcolor', "When set to `false` IntelliSense never shows `customcolor` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.folder': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.folder', "When set to `false` IntelliSense never shows `folder` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.typeParameter': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.typeParameter', "When set to `false` IntelliSense never shows `typeParameter` suggestions.")
|
||||
},
|
||||
'editor.suggest.filteredTypes.snippet': {
|
||||
type: 'boolean',
|
||||
default: true,
|
||||
markdownDescription: nls.localize('suggest.filtered.snippet', "When set to `false` IntelliSense never shows `snippet` suggestions.")
|
||||
},
|
||||
}
|
||||
);
|
||||
@@ -2452,6 +2521,7 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
||||
}
|
||||
const input = _input as ISuggestOptions;
|
||||
return {
|
||||
overwriteOnAccept: EditorBooleanOption.boolean(input.overwriteOnAccept, this.defaultValue.overwriteOnAccept),
|
||||
filterGraceful: EditorBooleanOption.boolean(input.filterGraceful, this.defaultValue.filterGraceful),
|
||||
snippetsPreventQuickSuggestions: EditorBooleanOption.boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
|
||||
localityBonus: EditorBooleanOption.boolean(input.localityBonus, this.defaultValue.localityBonus),
|
||||
@@ -2917,10 +2987,7 @@ export const EditorOptions = {
|
||||
{ description: nls.localize('fontFamily', "Controls the font family.") }
|
||||
)),
|
||||
fontInfo: register(new EditorFontInfo()),
|
||||
fontLigatures: register(new EditorBooleanOption(
|
||||
EditorOption.fontLigatures, 'fontLigatures', false,
|
||||
{ description: nls.localize('fontLigatures', "Enables/Disables font ligatures.") }
|
||||
)),
|
||||
fontLigatures2: register(new EditorFontLigatures()),
|
||||
fontSize: register(new EditorFontSize()),
|
||||
fontWeight: register(new EditorStringOption(
|
||||
EditorOption.fontWeight, 'fontWeight', EDITOR_FONT_DEFAULTS.fontWeight,
|
||||
|
||||
@@ -28,27 +28,29 @@ export class BareFontInfo {
|
||||
const fontFamily = options.get(EditorOption.fontFamily);
|
||||
const fontWeight = options.get(EditorOption.fontWeight);
|
||||
const fontSize = options.get(EditorOption.fontSize);
|
||||
const fontFeatureSettings = options.get(EditorOption.fontLigatures);
|
||||
const lineHeight = options.get(EditorOption.lineHeight);
|
||||
const letterSpacing = options.get(EditorOption.letterSpacing);
|
||||
return BareFontInfo._create(fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
|
||||
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static createFromRawSettings(opts: { fontFamily?: string; fontWeight?: string; fontSize?: number; lineHeight?: number; letterSpacing?: number; }, zoomLevel: number, ignoreEditorZoom: boolean = false): BareFontInfo {
|
||||
public static createFromRawSettings(opts: { fontFamily?: string; fontWeight?: string; fontSize?: number; fontLigatures?: boolean | string; lineHeight?: number; letterSpacing?: number; }, zoomLevel: number, ignoreEditorZoom: boolean = false): BareFontInfo {
|
||||
const fontFamily = EditorOptions.fontFamily.validate(opts.fontFamily);
|
||||
const fontWeight = EditorOptions.fontWeight.validate(opts.fontWeight);
|
||||
const fontSize = EditorOptions.fontSize.validate(opts.fontSize);
|
||||
const fontFeatureSettings = EditorOptions.fontLigatures2.validate(opts.fontLigatures);
|
||||
const lineHeight = EditorOptions.lineHeight.validate(opts.lineHeight);
|
||||
const letterSpacing = EditorOptions.letterSpacing.validate(opts.letterSpacing);
|
||||
return BareFontInfo._create(fontFamily, fontWeight, fontSize, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
|
||||
return BareFontInfo._create(fontFamily, fontWeight, fontSize, fontFeatureSettings, lineHeight, letterSpacing, zoomLevel, ignoreEditorZoom);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
private static _create(fontFamily: string, fontWeight: string, fontSize: number, lineHeight: number, letterSpacing: number, zoomLevel: number, ignoreEditorZoom: boolean): BareFontInfo {
|
||||
private static _create(fontFamily: string, fontWeight: string, fontSize: number, fontFeatureSettings: string, lineHeight: number, letterSpacing: number, zoomLevel: number, ignoreEditorZoom: boolean): BareFontInfo {
|
||||
if (lineHeight === 0) {
|
||||
lineHeight = Math.round(GOLDEN_LINE_HEIGHT_RATIO * fontSize);
|
||||
} else if (lineHeight < MINIMUM_LINE_HEIGHT) {
|
||||
@@ -64,6 +66,7 @@ export class BareFontInfo {
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: fontWeight,
|
||||
fontSize: fontSize,
|
||||
fontFeatureSettings: fontFeatureSettings,
|
||||
lineHeight: lineHeight,
|
||||
letterSpacing: letterSpacing
|
||||
});
|
||||
@@ -73,6 +76,7 @@ export class BareFontInfo {
|
||||
readonly fontFamily: string;
|
||||
readonly fontWeight: string;
|
||||
readonly fontSize: number;
|
||||
readonly fontFeatureSettings: string;
|
||||
readonly lineHeight: number;
|
||||
readonly letterSpacing: number;
|
||||
|
||||
@@ -84,6 +88,7 @@ export class BareFontInfo {
|
||||
fontFamily: string;
|
||||
fontWeight: string;
|
||||
fontSize: number;
|
||||
fontFeatureSettings: string;
|
||||
lineHeight: number;
|
||||
letterSpacing: number;
|
||||
}) {
|
||||
@@ -91,6 +96,7 @@ export class BareFontInfo {
|
||||
this.fontFamily = String(opts.fontFamily);
|
||||
this.fontWeight = String(opts.fontWeight);
|
||||
this.fontSize = opts.fontSize;
|
||||
this.fontFeatureSettings = opts.fontFeatureSettings;
|
||||
this.lineHeight = opts.lineHeight | 0;
|
||||
this.letterSpacing = opts.letterSpacing;
|
||||
}
|
||||
@@ -99,7 +105,7 @@ export class BareFontInfo {
|
||||
* @internal
|
||||
*/
|
||||
public getId(): string {
|
||||
return this.zoomLevel + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.lineHeight + '-' + this.letterSpacing;
|
||||
return this.zoomLevel + '-' + this.fontFamily + '-' + this.fontWeight + '-' + this.fontSize + '-' + this.fontFeatureSettings + '-' + this.lineHeight + '-' + this.letterSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +144,7 @@ export class FontInfo extends BareFontInfo {
|
||||
fontFamily: string;
|
||||
fontWeight: string;
|
||||
fontSize: number;
|
||||
fontFeatureSettings: string;
|
||||
lineHeight: number;
|
||||
letterSpacing: number;
|
||||
isMonospace: boolean;
|
||||
@@ -165,6 +172,7 @@ export class FontInfo extends BareFontInfo {
|
||||
this.fontFamily === other.fontFamily
|
||||
&& this.fontWeight === other.fontWeight
|
||||
&& this.fontSize === other.fontSize
|
||||
&& this.fontFeatureSettings === other.fontFeatureSettings
|
||||
&& this.lineHeight === other.lineHeight
|
||||
&& this.letterSpacing === other.letterSpacing
|
||||
&& this.typicalHalfwidthCharacterWidth === other.typicalHalfwidthCharacterWidth
|
||||
|
||||
@@ -153,7 +153,7 @@ class AutoClosedAction {
|
||||
|
||||
export class Cursor extends viewEvents.ViewEventEmitter implements ICursors {
|
||||
|
||||
public static MAX_CURSOR_COUNT = 10000;
|
||||
public static readonly MAX_CURSOR_COUNT = 10000;
|
||||
|
||||
private readonly _onDidReachMaxCursorCount: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidReachMaxCursorCount: Event<void> = this._onDidReachMaxCursorCount.event;
|
||||
|
||||
@@ -549,6 +549,29 @@ export class CursorColumns {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static toStatusbarColumn(lineContent: string, column: number, tabSize: number): number {
|
||||
let endOffset = lineContent.length;
|
||||
if (endOffset > column - 1) {
|
||||
endOffset = column - 1;
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
for (let i = 0; i < endOffset; i++) {
|
||||
let charCode = lineContent.charCodeAt(i);
|
||||
if (charCode === CharCode.Tab) {
|
||||
result = this.nextRenderTabStop(result, tabSize);
|
||||
} else {
|
||||
if (strings.isHighSurrogate(charCode)) {
|
||||
result = result + 1;
|
||||
i = i + 1;
|
||||
} else {
|
||||
result = result + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result + 1;
|
||||
}
|
||||
|
||||
public static visibleColumnFromColumn2(config: CursorConfiguration, model: ICursorSimpleModel, position: Position): number {
|
||||
return this.visibleColumnFromColumn(model.getLineContent(position.lineNumber), position.column, config.tabSize);
|
||||
}
|
||||
|
||||
@@ -122,20 +122,19 @@ export class CursorMoveCommands {
|
||||
for (let i = 0, len = cursors.length; i < len; i++) {
|
||||
const cursor = cursors[i];
|
||||
|
||||
const viewSelection = cursor.viewState.selection;
|
||||
const startLineNumber = viewSelection.startLineNumber;
|
||||
const lineCount = context.viewModel.getLineCount();
|
||||
const startLineNumber = cursor.modelState.selection.startLineNumber;
|
||||
const lineCount = context.model.getLineCount();
|
||||
|
||||
let endLineNumber = viewSelection.endLineNumber;
|
||||
let endLineNumber = cursor.modelState.selection.endLineNumber;
|
||||
let endColumn: number;
|
||||
if (endLineNumber === lineCount) {
|
||||
endColumn = context.viewModel.getLineMaxColumn(lineCount);
|
||||
endColumn = context.model.getLineMaxColumn(lineCount);
|
||||
} else {
|
||||
endLineNumber++;
|
||||
endColumn = 1;
|
||||
}
|
||||
|
||||
result[i] = CursorState.fromViewState(new SingleCursorState(
|
||||
result[i] = CursorState.fromModelState(new SingleCursorState(
|
||||
new Range(startLineNumber, 1, startLineNumber, 1), 0,
|
||||
new Position(endLineNumber, endColumn), 0
|
||||
));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition } from 'vs/editor/common/commands/replaceCommand';
|
||||
import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition, ReplaceCommandThatPreservesSelection } from 'vs/editor/common/commands/replaceCommand';
|
||||
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
|
||||
import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand';
|
||||
import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon';
|
||||
@@ -81,20 +81,17 @@ export class TypeOperations {
|
||||
const selection = selections[i];
|
||||
let position = selection.getPosition();
|
||||
|
||||
if (pasteOnNewLine && !selection.isEmpty()) {
|
||||
pasteOnNewLine = false;
|
||||
}
|
||||
if (pasteOnNewLine && text.indexOf('\n') !== text.length - 1) {
|
||||
pasteOnNewLine = false;
|
||||
}
|
||||
if (pasteOnNewLine && selection.startLineNumber !== selection.endLineNumber) {
|
||||
pasteOnNewLine = false;
|
||||
}
|
||||
if (pasteOnNewLine && selection.startColumn === model.getLineMinColumn(selection.startLineNumber) && selection.endColumn === model.getLineMaxColumn(selection.startLineNumber)) {
|
||||
pasteOnNewLine = false;
|
||||
}
|
||||
|
||||
if (pasteOnNewLine) {
|
||||
// Paste entire line at the beginning of line
|
||||
let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, 1);
|
||||
commands[i] = new ReplaceCommand(typeSelection, text);
|
||||
commands[i] = new ReplaceCommandThatPreservesSelection(typeSelection, text, selection);
|
||||
} else {
|
||||
commands[i] = new ReplaceCommand(selection, text);
|
||||
}
|
||||
@@ -462,6 +459,12 @@ export class TypeOperations {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do not over-type after a backslash
|
||||
const beforeCharacter = position.column > 2 ? lineText.charCodeAt(position.column - 2) : CharCode.Null;
|
||||
if (beforeCharacter === CharCode.Backslash) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must over-type a closing character typed by the editor
|
||||
if (config.autoClosingOvertype === 'auto') {
|
||||
let found = false;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toUint8 } from 'vs/editor/common/core/uint';
|
||||
import { toUint8 } from 'vs/base/common/uint';
|
||||
|
||||
/**
|
||||
* A fast character classifier that uses a compact array for ASCII values.
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
export class RGBA8 {
|
||||
_rgba8Brand: void;
|
||||
|
||||
static Empty = new RGBA8(0, 0, 0, 0);
|
||||
static readonly Empty = new RGBA8(0, 0, 0, 0);
|
||||
|
||||
/**
|
||||
* Red: integer in [0-255]
|
||||
|
||||
@@ -73,13 +73,6 @@ export class Selection extends Range {
|
||||
this.positionColumn = positionColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone this selection.
|
||||
*/
|
||||
public clone(): Selection {
|
||||
return new Selection(this.selectionStartLineNumber, this.selectionStartColumn, this.positionLineNumber, this.positionColumn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform to a human-readable representation.
|
||||
*/
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export class Uint8Matrix {
|
||||
|
||||
private readonly _data: Uint8Array;
|
||||
public readonly rows: number;
|
||||
public readonly cols: number;
|
||||
|
||||
constructor(rows: number, cols: number, defaultValue: number) {
|
||||
const data = new Uint8Array(rows * cols);
|
||||
for (let i = 0, len = rows * cols; i < len; i++) {
|
||||
data[i] = defaultValue;
|
||||
}
|
||||
|
||||
this._data = data;
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
}
|
||||
|
||||
public get(row: number, col: number): number {
|
||||
return this._data[row * this.cols + col];
|
||||
}
|
||||
|
||||
public set(row: number, col: number, value: number): void {
|
||||
this._data[row * this.cols + col] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Constants {
|
||||
/**
|
||||
* MAX SMI (SMall Integer) as defined in v8.
|
||||
* one bit is lost for boxing/unboxing flag.
|
||||
* one bit is lost for sign flag.
|
||||
* See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
|
||||
*/
|
||||
MAX_SAFE_SMALL_INTEGER = 1 << 30,
|
||||
|
||||
/**
|
||||
* MIN SMI (SMall Integer) as defined in v8.
|
||||
* one bit is lost for boxing/unboxing flag.
|
||||
* one bit is lost for sign flag.
|
||||
* See https://thibaultlaurens.github.io/javascript/2013/04/29/how-the-v8-engine-works/#tagged-values
|
||||
*/
|
||||
MIN_SAFE_SMALL_INTEGER = -(1 << 30),
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 8 bits.
|
||||
*/
|
||||
MAX_UINT_8 = 255, // 2^8 - 1
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 16 bits.
|
||||
*/
|
||||
MAX_UINT_16 = 65535, // 2^16 - 1
|
||||
|
||||
/**
|
||||
* Max unsigned integer that fits on 32 bits.
|
||||
*/
|
||||
MAX_UINT_32 = 4294967295, // 2^32 - 1
|
||||
|
||||
|
||||
}
|
||||
|
||||
export function toUint8(v: number): number {
|
||||
if (v < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (v > Constants.MAX_UINT_8) {
|
||||
return Constants.MAX_UINT_8;
|
||||
}
|
||||
return v | 0;
|
||||
}
|
||||
|
||||
export function toUint32(v: number): number {
|
||||
if (v < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (v > Constants.MAX_UINT_32) {
|
||||
return Constants.MAX_UINT_32;
|
||||
}
|
||||
return v | 0;
|
||||
}
|
||||
|
||||
export function toUint32Array(arr: number[]): Uint32Array {
|
||||
const len = arr.length;
|
||||
const r = new Uint32Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
r[i] = toUint32(arr[i]);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
@@ -3,83 +3,63 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDiffChange, ISequence, LcsDiff } from 'vs/base/common/diff/diff';
|
||||
import { IDiffChange, ISequence, LcsDiff, IDiffResult } from 'vs/base/common/diff/diff';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ICharChange, ILineChange } from 'vs/editor/common/editorCommon';
|
||||
|
||||
const MAXIMUM_RUN_TIME = 5000; // 5 seconds
|
||||
const MINIMUM_MATCHING_CHARACTER_LENGTH = 3;
|
||||
|
||||
function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: () => boolean, pretty: boolean): IDiffChange[] {
|
||||
export interface IDiffComputerResult {
|
||||
quitEarly: boolean;
|
||||
changes: ILineChange[];
|
||||
}
|
||||
|
||||
function computeDiff(originalSequence: ISequence, modifiedSequence: ISequence, continueProcessingPredicate: () => boolean, pretty: boolean): IDiffResult {
|
||||
const diffAlgo = new LcsDiff(originalSequence, modifiedSequence, continueProcessingPredicate);
|
||||
return diffAlgo.ComputeDiff(pretty);
|
||||
}
|
||||
|
||||
class LineMarkerSequence implements ISequence {
|
||||
class LineSequence implements ISequence {
|
||||
|
||||
private readonly _lines: string[];
|
||||
public readonly lines: string[];
|
||||
private readonly _startColumns: number[];
|
||||
private readonly _endColumns: number[];
|
||||
|
||||
constructor(lines: string[]) {
|
||||
let startColumns: number[] = [];
|
||||
let endColumns: number[] = [];
|
||||
const startColumns: number[] = [];
|
||||
const 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);
|
||||
startColumns[i] = getFirstNonBlankColumn(lines[i], 1);
|
||||
endColumns[i] = getLastNonBlankColumn(lines[i], 1);
|
||||
}
|
||||
this._lines = lines;
|
||||
this.lines = lines;
|
||||
this._startColumns = startColumns;
|
||||
this._endColumns = endColumns;
|
||||
}
|
||||
|
||||
public getLength(): number {
|
||||
return this._lines.length;
|
||||
}
|
||||
|
||||
public getElementAtIndex(i: number): string {
|
||||
return this._lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1);
|
||||
public getElements(): Int32Array | number[] | string[] {
|
||||
const elements: string[] = [];
|
||||
for (let i = 0, len = this.lines.length; i < len; i++) {
|
||||
elements[i] = this.lines[i].substring(this._startColumns[i] - 1, this._endColumns[i] - 1);
|
||||
}
|
||||
return elements;
|
||||
}
|
||||
|
||||
public getStartLineNumber(i: number): number {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
public getStartColumn(i: number): number {
|
||||
return this._startColumns[i];
|
||||
}
|
||||
|
||||
public getEndLineNumber(i: number): number {
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
public getEndColumn(i: number): number {
|
||||
return this._endColumns[i];
|
||||
}
|
||||
|
||||
public static _getFirstNonBlankColumn(txt: string, defaultValue: number): number {
|
||||
const r = strings.firstNonWhitespaceIndex(txt);
|
||||
if (r === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
public static _getLastNonBlankColumn(txt: string, defaultValue: number): number {
|
||||
const r = strings.lastNonWhitespaceIndex(txt);
|
||||
if (r === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return r + 2;
|
||||
}
|
||||
|
||||
public getCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence {
|
||||
let charCodes: number[] = [];
|
||||
let lineNumbers: number[] = [];
|
||||
let columns: number[] = [];
|
||||
public createCharSequence(shouldIgnoreTrimWhitespace: boolean, startIndex: number, endIndex: number): CharSequence {
|
||||
const charCodes: number[] = [];
|
||||
const lineNumbers: number[] = [];
|
||||
const columns: number[] = [];
|
||||
let len = 0;
|
||||
for (let index = startIndex; index <= endIndex; index++) {
|
||||
const lineContent = this._lines[index];
|
||||
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++) {
|
||||
@@ -105,12 +85,8 @@ class CharSequence implements ISequence {
|
||||
this._columns = columns;
|
||||
}
|
||||
|
||||
public getLength(): number {
|
||||
return this._charCodes.length;
|
||||
}
|
||||
|
||||
public getElementAtIndex(i: number): number {
|
||||
return this._charCodes[i];
|
||||
public getElements(): Int32Array | number[] | string[] {
|
||||
return this._charCodes;
|
||||
}
|
||||
|
||||
public getStartLineNumber(i: number): number {
|
||||
@@ -208,7 +184,7 @@ function postProcessCharChanges(rawChanges: IDiffChange[]): IDiffChange[] {
|
||||
return rawChanges;
|
||||
}
|
||||
|
||||
let result = [rawChanges[0]];
|
||||
const result = [rawChanges[0]];
|
||||
let prevChange = result[0];
|
||||
|
||||
for (let i = 1, len = rawChanges.length; i < len; i++) {
|
||||
@@ -254,7 +230,7 @@ class LineChange implements ILineChange {
|
||||
this.charChanges = charChanges;
|
||||
}
|
||||
|
||||
public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineMarkerSequence, modifiedLineSequence: LineMarkerSequence, continueProcessingPredicate: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange {
|
||||
public static createFromDiffResult(shouldIgnoreTrimWhitespace: boolean, diffChange: IDiffChange, originalLineSequence: LineSequence, modifiedLineSequence: LineSequence, continueCharDiff: () => boolean, shouldComputeCharChanges: boolean, shouldPostProcessCharChanges: boolean): LineChange {
|
||||
let originalStartLineNumber: number;
|
||||
let originalEndLineNumber: number;
|
||||
let modifiedStartLineNumber: number;
|
||||
@@ -277,11 +253,12 @@ class LineChange implements ILineChange {
|
||||
modifiedEndLineNumber = modifiedLineSequence.getEndLineNumber(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);
|
||||
if (shouldComputeCharChanges && diffChange.originalLength > 0 && diffChange.originalLength < 20 && diffChange.modifiedLength > 0 && diffChange.modifiedLength < 20 && continueCharDiff()) {
|
||||
// Compute character changes for diff chunks of at most 20 lines...
|
||||
const originalCharSequence = originalLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength - 1);
|
||||
const modifiedCharSequence = modifiedLineSequence.createCharSequence(shouldIgnoreTrimWhitespace, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength - 1);
|
||||
|
||||
let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueProcessingPredicate, true);
|
||||
let rawChanges = computeDiff(originalCharSequence, modifiedCharSequence, continueCharDiff, true).changes;
|
||||
|
||||
if (shouldPostProcessCharChanges) {
|
||||
rawChanges = postProcessCharChanges(rawChanges);
|
||||
@@ -302,6 +279,7 @@ export interface IDiffComputerOpts {
|
||||
shouldPostProcessCharChanges: boolean;
|
||||
shouldIgnoreTrimWhitespace: boolean;
|
||||
shouldMakePrettyDiff: boolean;
|
||||
maxComputationTime: number;
|
||||
}
|
||||
|
||||
export class DiffComputer {
|
||||
@@ -310,88 +288,96 @@ export class DiffComputer {
|
||||
private readonly shouldPostProcessCharChanges: boolean;
|
||||
private readonly shouldIgnoreTrimWhitespace: boolean;
|
||||
private readonly shouldMakePrettyDiff: boolean;
|
||||
private readonly maximumRunTimeMs: number;
|
||||
private readonly originalLines: string[];
|
||||
private readonly modifiedLines: string[];
|
||||
private readonly original: LineMarkerSequence;
|
||||
private readonly modified: LineMarkerSequence;
|
||||
|
||||
private computationStartTime: number;
|
||||
private readonly original: LineSequence;
|
||||
private readonly modified: LineSequence;
|
||||
private readonly continueLineDiff: () => boolean;
|
||||
private readonly continueCharDiff: () => boolean;
|
||||
|
||||
constructor(originalLines: string[], modifiedLines: string[], opts: IDiffComputerOpts) {
|
||||
this.shouldComputeCharChanges = opts.shouldComputeCharChanges;
|
||||
this.shouldPostProcessCharChanges = opts.shouldPostProcessCharChanges;
|
||||
this.shouldIgnoreTrimWhitespace = opts.shouldIgnoreTrimWhitespace;
|
||||
this.shouldMakePrettyDiff = opts.shouldMakePrettyDiff;
|
||||
this.maximumRunTimeMs = MAXIMUM_RUN_TIME;
|
||||
this.originalLines = originalLines;
|
||||
this.modifiedLines = modifiedLines;
|
||||
this.original = new LineMarkerSequence(originalLines);
|
||||
this.modified = new LineMarkerSequence(modifiedLines);
|
||||
this.original = new LineSequence(originalLines);
|
||||
this.modified = new LineSequence(modifiedLines);
|
||||
|
||||
this.computationStartTime = (new Date()).getTime();
|
||||
this.continueLineDiff = createContinueProcessingPredicate(opts.maxComputationTime);
|
||||
this.continueCharDiff = createContinueProcessingPredicate(opts.maxComputationTime === 0 ? 0 : Math.min(opts.maxComputationTime, 5000)); // never run after 5s for character changes...
|
||||
}
|
||||
|
||||
public computeDiff(): ILineChange[] {
|
||||
public computeDiff(): IDiffComputerResult {
|
||||
|
||||
if (this.original.getLength() === 1 && this.original.getElementAtIndex(0).length === 0) {
|
||||
if (this.original.lines.length === 1 && this.original.lines[0].length === 0) {
|
||||
// empty original => fast path
|
||||
return [{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: 1,
|
||||
modifiedStartLineNumber: 1,
|
||||
modifiedEndLineNumber: this.modified.getLength(),
|
||||
charChanges: [{
|
||||
modifiedEndColumn: 0,
|
||||
modifiedEndLineNumber: 0,
|
||||
modifiedStartColumn: 0,
|
||||
modifiedStartLineNumber: 0,
|
||||
originalEndColumn: 0,
|
||||
originalEndLineNumber: 0,
|
||||
originalStartColumn: 0,
|
||||
originalStartLineNumber: 0
|
||||
return {
|
||||
quitEarly: false,
|
||||
changes: [{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: 1,
|
||||
modifiedStartLineNumber: 1,
|
||||
modifiedEndLineNumber: this.modified.lines.length,
|
||||
charChanges: [{
|
||||
modifiedEndColumn: 0,
|
||||
modifiedEndLineNumber: 0,
|
||||
modifiedStartColumn: 0,
|
||||
modifiedStartLineNumber: 0,
|
||||
originalEndColumn: 0,
|
||||
originalEndLineNumber: 0,
|
||||
originalStartColumn: 0,
|
||||
originalStartLineNumber: 0
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
if (this.modified.getLength() === 1 && this.modified.getElementAtIndex(0).length === 0) {
|
||||
if (this.modified.lines.length === 1 && this.modified.lines[0].length === 0) {
|
||||
// empty modified => fast path
|
||||
return [{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: this.original.getLength(),
|
||||
modifiedStartLineNumber: 1,
|
||||
modifiedEndLineNumber: 1,
|
||||
charChanges: [{
|
||||
modifiedEndColumn: 0,
|
||||
modifiedEndLineNumber: 0,
|
||||
modifiedStartColumn: 0,
|
||||
modifiedStartLineNumber: 0,
|
||||
originalEndColumn: 0,
|
||||
originalEndLineNumber: 0,
|
||||
originalStartColumn: 0,
|
||||
originalStartLineNumber: 0
|
||||
return {
|
||||
quitEarly: false,
|
||||
changes: [{
|
||||
originalStartLineNumber: 1,
|
||||
originalEndLineNumber: this.original.lines.length,
|
||||
modifiedStartLineNumber: 1,
|
||||
modifiedEndLineNumber: 1,
|
||||
charChanges: [{
|
||||
modifiedEndColumn: 0,
|
||||
modifiedEndLineNumber: 0,
|
||||
modifiedStartColumn: 0,
|
||||
modifiedStartLineNumber: 0,
|
||||
originalEndColumn: 0,
|
||||
originalEndLineNumber: 0,
|
||||
originalStartColumn: 0,
|
||||
originalStartLineNumber: 0
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
this.computationStartTime = (new Date()).getTime();
|
||||
|
||||
let rawChanges = computeDiff(this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldMakePrettyDiff);
|
||||
const diffResult = computeDiff(this.original, this.modified, this.continueLineDiff, this.shouldMakePrettyDiff);
|
||||
const rawChanges = diffResult.changes;
|
||||
const quitEarly = diffResult.quitEarly;
|
||||
|
||||
// The diff is always computed with ignoring trim whitespace
|
||||
// This ensures we get the prettiest diff
|
||||
|
||||
if (this.shouldIgnoreTrimWhitespace) {
|
||||
let lineChanges: LineChange[] = [];
|
||||
const lineChanges: LineChange[] = [];
|
||||
for (let i = 0, length = rawChanges.length; i < length; i++) {
|
||||
lineChanges.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, rawChanges[i], this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
|
||||
lineChanges.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, rawChanges[i], this.original, this.modified, this.continueCharDiff, this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
|
||||
}
|
||||
return lineChanges;
|
||||
return {
|
||||
quitEarly: quitEarly,
|
||||
changes: lineChanges
|
||||
};
|
||||
}
|
||||
|
||||
// Need to post-process and introduce changes where the trim whitespace is different
|
||||
// Note that we are looping starting at -1 to also cover the lines before the first change
|
||||
let result: LineChange[] = [];
|
||||
const result: LineChange[] = [];
|
||||
|
||||
let originalLineIndex = 0;
|
||||
let modifiedLineIndex = 0;
|
||||
@@ -409,8 +395,8 @@ export class DiffComputer {
|
||||
|
||||
// Check the leading whitespace
|
||||
{
|
||||
let originalStartColumn = LineMarkerSequence._getFirstNonBlankColumn(originalLine, 1);
|
||||
let modifiedStartColumn = LineMarkerSequence._getFirstNonBlankColumn(modifiedLine, 1);
|
||||
let originalStartColumn = getFirstNonBlankColumn(originalLine, 1);
|
||||
let modifiedStartColumn = getFirstNonBlankColumn(modifiedLine, 1);
|
||||
while (originalStartColumn > 1 && modifiedStartColumn > 1) {
|
||||
const originalChar = originalLine.charCodeAt(originalStartColumn - 2);
|
||||
const modifiedChar = modifiedLine.charCodeAt(modifiedStartColumn - 2);
|
||||
@@ -431,8 +417,8 @@ export class DiffComputer {
|
||||
|
||||
// Check the trailing whitespace
|
||||
{
|
||||
let originalEndColumn = LineMarkerSequence._getLastNonBlankColumn(originalLine, 1);
|
||||
let modifiedEndColumn = LineMarkerSequence._getLastNonBlankColumn(modifiedLine, 1);
|
||||
let originalEndColumn = getLastNonBlankColumn(originalLine, 1);
|
||||
let modifiedEndColumn = getLastNonBlankColumn(modifiedLine, 1);
|
||||
const originalMaxColumn = originalLine.length + 1;
|
||||
const modifiedMaxColumn = modifiedLine.length + 1;
|
||||
while (originalEndColumn < originalMaxColumn && modifiedEndColumn < modifiedMaxColumn) {
|
||||
@@ -459,14 +445,17 @@ export class DiffComputer {
|
||||
|
||||
if (nextChange) {
|
||||
// Emit the actual change
|
||||
result.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, nextChange, this.original, this.modified, this._continueProcessingPredicate.bind(this), this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
|
||||
result.push(LineChange.createFromDiffResult(this.shouldIgnoreTrimWhitespace, nextChange, this.original, this.modified, this.continueCharDiff, this.shouldComputeCharChanges, this.shouldPostProcessCharChanges));
|
||||
|
||||
originalLineIndex += nextChange.originalLength;
|
||||
modifiedLineIndex += nextChange.modifiedLength;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return {
|
||||
quitEarly: quitEarly,
|
||||
changes: result
|
||||
};
|
||||
}
|
||||
|
||||
private _pushTrimWhitespaceCharChange(
|
||||
@@ -513,8 +502,8 @@ export class DiffComputer {
|
||||
if (prevChange.originalEndLineNumber + 1 === originalLineNumber && prevChange.modifiedEndLineNumber + 1 === modifiedLineNumber) {
|
||||
prevChange.originalEndLineNumber = originalLineNumber;
|
||||
prevChange.modifiedEndLineNumber = modifiedLineNumber;
|
||||
if (this.shouldComputeCharChanges) {
|
||||
prevChange.charChanges!.push(new CharChange(
|
||||
if (this.shouldComputeCharChanges && prevChange.charChanges) {
|
||||
prevChange.charChanges.push(new CharChange(
|
||||
originalLineNumber, originalStartColumn, originalLineNumber, originalEndColumn,
|
||||
modifiedLineNumber, modifiedStartColumn, modifiedLineNumber, modifiedEndColumn
|
||||
));
|
||||
@@ -524,13 +513,31 @@ export class DiffComputer {
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private _continueProcessingPredicate(): boolean {
|
||||
if (this.maximumRunTimeMs === 0) {
|
||||
return true;
|
||||
}
|
||||
const now = (new Date()).getTime();
|
||||
return now - this.computationStartTime < this.maximumRunTimeMs;
|
||||
function getFirstNonBlankColumn(txt: string, defaultValue: number): number {
|
||||
const r = strings.firstNonWhitespaceIndex(txt);
|
||||
if (r === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return r + 1;
|
||||
}
|
||||
|
||||
function getLastNonBlankColumn(txt: string, defaultValue: number): number {
|
||||
const r = strings.lastNonWhitespaceIndex(txt);
|
||||
if (r === -1) {
|
||||
return defaultValue;
|
||||
}
|
||||
return r + 2;
|
||||
}
|
||||
|
||||
function createContinueProcessingPredicate(maximumRuntime: number): () => boolean {
|
||||
if (maximumRuntime === 0) {
|
||||
return () => true;
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
return () => {
|
||||
return Date.now() - startTime < maximumRuntime;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -315,6 +315,12 @@ export interface IEditor {
|
||||
*/
|
||||
getVisibleColumnFromPosition(position: IPosition): number;
|
||||
|
||||
/**
|
||||
* Given a position, returns a column number that takes tab-widths into account.
|
||||
* @internal
|
||||
*/
|
||||
getStatusbarColumn(position: IPosition): number;
|
||||
|
||||
/**
|
||||
* Returns the primary position of the cursor.
|
||||
*/
|
||||
@@ -488,10 +494,6 @@ export interface IDiffEditor extends IEditor {
|
||||
* An editor contribution that gets created every time a new editor gets created and gets disposed when the editor gets disposed.
|
||||
*/
|
||||
export interface IEditorContribution {
|
||||
/**
|
||||
* Get a unique identifier for this contribution.
|
||||
*/
|
||||
getId(): string;
|
||||
/**
|
||||
* Dispose this contribution.
|
||||
*/
|
||||
@@ -506,6 +508,17 @@ export interface IEditorContribution {
|
||||
restoreViewState?(state: any): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* A diff editor contribution that gets created every time a new diffeditor gets created and gets disposed when the diff editor gets disposed.
|
||||
* @internal
|
||||
*/
|
||||
export interface IDiffEditorContribution {
|
||||
/**
|
||||
* Dispose this contribution.
|
||||
*/
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
@@ -459,8 +459,8 @@ export class FindMatch {
|
||||
*/
|
||||
export interface IFoundBracket {
|
||||
range: Range;
|
||||
open: string;
|
||||
close: string;
|
||||
open: string[];
|
||||
close: string[];
|
||||
isOpen: boolean;
|
||||
}
|
||||
|
||||
@@ -608,6 +608,12 @@ export interface ITextModel {
|
||||
*/
|
||||
getValueLengthInRange(range: IRange): number;
|
||||
|
||||
/**
|
||||
* Get the character count of text in a certain range.
|
||||
* @param range The range describing what text length to get.
|
||||
*/
|
||||
getCharacterCountInRange(range: IRange): number;
|
||||
|
||||
/**
|
||||
* Splits characters in two buckets. First bucket (A) is of characters that
|
||||
* sit in lines with length < `LONG_LINE_BOUNDARY`. Second bucket (B) is of
|
||||
@@ -881,6 +887,13 @@ export interface ITextModel {
|
||||
*/
|
||||
findNextBracket(position: IPosition): IFoundBracket | null;
|
||||
|
||||
/**
|
||||
* Find the enclosing brackets that contain `position`.
|
||||
* @param position The position at which to start the search.
|
||||
* @internal
|
||||
*/
|
||||
findEnclosingBrackets(position: IPosition): [Range, Range] | null;
|
||||
|
||||
/**
|
||||
* Given a `position`, if the position is on top or near a bracket,
|
||||
* find the matching bracket of that bracket and return the ranges of both brackets.
|
||||
@@ -1196,6 +1209,7 @@ export interface ITextBuffer {
|
||||
getValueInRange(range: Range, eol: EndOfLinePreference): string;
|
||||
createSnapshot(preserveBOM: boolean): ITextSnapshot;
|
||||
getValueLengthInRange(range: Range, eol: EndOfLinePreference): number;
|
||||
getCharacterCountInRange(range: Range, eol: EndOfLinePreference): number;
|
||||
getLength(): number;
|
||||
getLineCount(): number;
|
||||
getLinesContent(): string[];
|
||||
|
||||
@@ -106,6 +106,37 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
return endOffset - startOffset;
|
||||
}
|
||||
|
||||
public getCharacterCountInRange(range: Range, eol: EndOfLinePreference = EndOfLinePreference.TextDefined): number {
|
||||
if (this._mightContainNonBasicASCII) {
|
||||
// we must count by iterating
|
||||
|
||||
let result = 0;
|
||||
|
||||
const fromLineNumber = range.startLineNumber;
|
||||
const toLineNumber = range.endLineNumber;
|
||||
for (let lineNumber = fromLineNumber; lineNumber <= toLineNumber; lineNumber++) {
|
||||
const lineContent = this.getLineContent(lineNumber);
|
||||
const fromOffset = (lineNumber === fromLineNumber ? range.startColumn - 1 : 0);
|
||||
const toOffset = (lineNumber === toLineNumber ? range.endColumn - 1 : lineContent.length);
|
||||
|
||||
for (let offset = fromOffset; offset < toOffset; offset++) {
|
||||
if (strings.isHighSurrogate(lineContent.charCodeAt(offset))) {
|
||||
result = result + 1;
|
||||
offset = offset + 1;
|
||||
} else {
|
||||
result = result + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result += this._getEndOfLine(eol).length * (toLineNumber - fromLineNumber);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return this.getValueLengthInRange(range, eol);
|
||||
}
|
||||
|
||||
public getLength(): number {
|
||||
return this._pieceTree.getLength();
|
||||
}
|
||||
|
||||
@@ -726,6 +726,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._buffer.getValueLengthInRange(this.validateRange(rawRange), eol);
|
||||
}
|
||||
|
||||
public getCharacterCountInRange(rawRange: IRange, eol: model.EndOfLinePreference = model.EndOfLinePreference.TextDefined): number {
|
||||
this._assertNotDisposed();
|
||||
return this._buffer.getCharacterCountInRange(this.validateRange(rawRange), eol);
|
||||
}
|
||||
|
||||
public getLineCount(): number {
|
||||
this._assertNotDisposed();
|
||||
return this._buffer.getLineCount();
|
||||
@@ -1910,7 +1915,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
if (tokenIndex < 0) {
|
||||
return null;
|
||||
}
|
||||
@@ -1919,15 +1924,15 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
// check that the token is not to be ignored
|
||||
if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) {
|
||||
// limit search to not go before `maxBracketLength`
|
||||
let searchStartOffset = Math.max(lineTokens.getStartOffset(tokenIndex), position.column - 1 - currentModeBrackets.maxBracketLength);
|
||||
let searchStartOffset = Math.max(0, position.column - 1 - currentModeBrackets.maxBracketLength);
|
||||
// limit search to not go after `maxBracketLength`
|
||||
const searchEndOffset = Math.min(lineTokens.getEndOffset(tokenIndex), position.column - 1 + currentModeBrackets.maxBracketLength);
|
||||
const searchEndOffset = Math.min(lineText.length, position.column - 1 + currentModeBrackets.maxBracketLength);
|
||||
|
||||
// it might be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets
|
||||
// `bestResult` will contain the most right-side result
|
||||
let bestResult: [Range, Range] | null = null;
|
||||
while (true) {
|
||||
let foundBracket = BracketsUtils.findNextBracketInToken(currentModeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
const foundBracket = BracketsUtils.findNextBracketInRange(currentModeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (!foundBracket) {
|
||||
// there are no more brackets in this text
|
||||
break;
|
||||
@@ -1935,12 +1940,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
// check that we didn't hit a bracket too far away from position
|
||||
if (foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
|
||||
let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1);
|
||||
foundBracketText = foundBracketText.toLowerCase();
|
||||
|
||||
let r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]);
|
||||
|
||||
// check that we can actually match this bracket
|
||||
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
|
||||
const r = this._matchFoundBracket(foundBracket, currentModeBrackets.textIsBracket[foundBracketText], currentModeBrackets.textIsOpenBracket[foundBracketText]);
|
||||
if (r) {
|
||||
bestResult = r;
|
||||
}
|
||||
@@ -1956,24 +1957,20 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
// If position is in between two tokens, try also looking in the previous token
|
||||
if (tokenIndex > 0 && lineTokens.getStartOffset(tokenIndex) === position.column - 1) {
|
||||
const searchEndOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
tokenIndex--;
|
||||
const prevModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(lineTokens.getLanguageId(tokenIndex));
|
||||
const prevTokenIndex = tokenIndex - 1;
|
||||
const prevModeBrackets = LanguageConfigurationRegistry.getBracketsSupport(lineTokens.getLanguageId(prevTokenIndex));
|
||||
|
||||
// check that previous token is not to be ignored
|
||||
if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) {
|
||||
if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) {
|
||||
// limit search in case previous token is very large, there's no need to go beyond `maxBracketLength`
|
||||
const searchStartOffset = Math.max(lineTokens.getStartOffset(tokenIndex), position.column - 1 - prevModeBrackets.maxBracketLength);
|
||||
const foundBracket = BracketsUtils.findPrevBracketInToken(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
const searchStartOffset = Math.max(0, position.column - 1 - prevModeBrackets.maxBracketLength);
|
||||
const searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength);
|
||||
const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
|
||||
// check that we didn't hit a bracket too far away from position
|
||||
if (foundBracket && foundBracket.startColumn <= position.column && position.column <= foundBracket.endColumn) {
|
||||
let foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1);
|
||||
foundBracketText = foundBracketText.toLowerCase();
|
||||
|
||||
let r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]);
|
||||
|
||||
// check that we can actually match this bracket
|
||||
const foundBracketText = lineText.substring(foundBracket.startColumn - 1, foundBracket.endColumn - 1).toLowerCase();
|
||||
const r = this._matchFoundBracket(foundBracket, prevModeBrackets.textIsBracket[foundBracketText], prevModeBrackets.textIsOpenBracket[foundBracketText]);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
@@ -2011,54 +2008,76 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const reversedBracketRegex = bracket.reversedRegex;
|
||||
let count = -1;
|
||||
|
||||
const searchPrevMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
|
||||
while (true) {
|
||||
const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
|
||||
const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
|
||||
if (bracket.isOpen(hitText)) {
|
||||
count++;
|
||||
} else if (bracket.isClose(hitText)) {
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count === 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
searchEndOffset = r.startColumn - 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = tokenCount - 1;
|
||||
let searchStopOffset = -1;
|
||||
let searchStartOffset = lineText.length;
|
||||
let searchEndOffset = lineText.length;
|
||||
if (lineNumber === position.lineNumber) {
|
||||
tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
searchStopOffset = position.column - 1;
|
||||
searchStartOffset = position.column - 1;
|
||||
searchEndOffset = position.column - 1;
|
||||
}
|
||||
|
||||
let prevSearchInToken = true;
|
||||
for (; tokenIndex >= 0; tokenIndex--) {
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const searchInToken = (lineTokens.getLanguageId(tokenIndex) === languageId && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
|
||||
|
||||
if (searchStopOffset === -1) {
|
||||
searchStopOffset = tokenEndOffset;
|
||||
}
|
||||
|
||||
if (tokenLanguageId === languageId && !ignoreBracketsInToken(tokenType)) {
|
||||
|
||||
while (true) {
|
||||
let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, lineNumber, lineText, tokenStartOffset, searchStopOffset);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
|
||||
let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1);
|
||||
hitText = hitText.toLowerCase();
|
||||
|
||||
if (hitText === bracket.open) {
|
||||
count++;
|
||||
} else if (hitText === bracket.close) {
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count === 0) {
|
||||
if (searchInToken) {
|
||||
// this token should be searched
|
||||
if (prevSearchInToken) {
|
||||
// the previous token should be searched, simply extend searchStartOffset
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
} else {
|
||||
// the previous token should not be searched
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
}
|
||||
} else {
|
||||
// this token should not be searched
|
||||
if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchPrevMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
searchStopOffset = r.startColumn - 1;
|
||||
}
|
||||
}
|
||||
|
||||
searchStopOffset = -1;
|
||||
prevSearchInToken = searchInToken;
|
||||
}
|
||||
|
||||
if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchPrevMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2072,53 +2091,77 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const bracketRegex = bracket.forwardRegex;
|
||||
let count = 1;
|
||||
|
||||
for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) {
|
||||
const searchNextMatchingBracketInRange = (lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): Range | null => {
|
||||
while (true) {
|
||||
const r = BracketsUtils.findNextBracketInRange(bracketRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
|
||||
const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
|
||||
if (bracket.isOpen(hitText)) {
|
||||
count++;
|
||||
} else if (bracket.isClose(hitText)) {
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count === 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
searchStartOffset = r.endColumn - 1;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const lineCount = this.getLineCount();
|
||||
for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = 0;
|
||||
let searchStartOffset = 0;
|
||||
let searchEndOffset = 0;
|
||||
if (lineNumber === position.lineNumber) {
|
||||
tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
searchStartOffset = position.column - 1;
|
||||
searchEndOffset = position.column - 1;
|
||||
}
|
||||
|
||||
let prevSearchInToken = true;
|
||||
for (; tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
const searchInToken = (lineTokens.getLanguageId(tokenIndex) === languageId && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
|
||||
|
||||
if (searchStartOffset === 0) {
|
||||
searchStartOffset = tokenStartOffset;
|
||||
}
|
||||
|
||||
if (tokenLanguageId === languageId && !ignoreBracketsInToken(tokenType)) {
|
||||
while (true) {
|
||||
let r = BracketsUtils.findNextBracketInToken(bracketRegex, lineNumber, lineText, searchStartOffset, tokenEndOffset);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
|
||||
let hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1);
|
||||
hitText = hitText.toLowerCase();
|
||||
|
||||
if (hitText === bracket.open) {
|
||||
count++;
|
||||
} else if (hitText === bracket.close) {
|
||||
count--;
|
||||
}
|
||||
|
||||
if (count === 0) {
|
||||
if (searchInToken) {
|
||||
// this token should be searched
|
||||
if (prevSearchInToken) {
|
||||
// the previous token should be searched, simply extend searchEndOffset
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
} else {
|
||||
// the previous token should not be searched
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
}
|
||||
} else {
|
||||
// this token should not be searched
|
||||
if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchNextMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
searchStartOffset = r.endColumn - 1;
|
||||
}
|
||||
}
|
||||
|
||||
searchStartOffset = 0;
|
||||
prevSearchInToken = searchInToken;
|
||||
}
|
||||
|
||||
if (prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchNextMatchingBracketInRange(lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2136,33 +2179,66 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = tokenCount - 1;
|
||||
let searchStopOffset = -1;
|
||||
let searchStartOffset = lineText.length;
|
||||
let searchEndOffset = lineText.length;
|
||||
if (lineNumber === position.lineNumber) {
|
||||
tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
searchStopOffset = position.column - 1;
|
||||
}
|
||||
|
||||
for (; tokenIndex >= 0; tokenIndex--) {
|
||||
searchStartOffset = position.column - 1;
|
||||
searchEndOffset = position.column - 1;
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
|
||||
if (searchStopOffset === -1) {
|
||||
searchStopOffset = tokenEndOffset;
|
||||
}
|
||||
if (languageId !== tokenLanguageId) {
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
}
|
||||
if (modeBrackets && !ignoreBracketsInToken(tokenType)) {
|
||||
let r = BracketsUtils.findPrevBracketInToken(modeBrackets.reversedRegex, lineNumber, lineText, tokenStartOffset, searchStopOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
|
||||
let prevSearchInToken = true;
|
||||
for (; tokenIndex >= 0; tokenIndex--) {
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
|
||||
if (languageId !== tokenLanguageId) {
|
||||
// language id change!
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
prevSearchInToken = false;
|
||||
}
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
}
|
||||
|
||||
const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
|
||||
|
||||
if (searchInToken) {
|
||||
// this token should be searched
|
||||
if (prevSearchInToken) {
|
||||
// the previous token should be searched, simply extend searchStartOffset
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
} else {
|
||||
// the previous token should not be searched
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
}
|
||||
} else {
|
||||
// this token should not be searched
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchStopOffset = -1;
|
||||
prevSearchInToken = searchInToken;
|
||||
}
|
||||
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findPrevBracketInRange(modeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2171,43 +2247,187 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
public findNextBracket(_position: IPosition): model.IFoundBracket | null {
|
||||
const position = this.validatePosition(_position);
|
||||
const lineCount = this.getLineCount();
|
||||
|
||||
let languageId: LanguageId = -1;
|
||||
let modeBrackets: RichEditBrackets | null = null;
|
||||
for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) {
|
||||
for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = 0;
|
||||
let searchStartOffset = 0;
|
||||
let searchEndOffset = 0;
|
||||
if (lineNumber === position.lineNumber) {
|
||||
tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
searchStartOffset = position.column - 1;
|
||||
}
|
||||
|
||||
for (; tokenIndex < tokenCount; tokenIndex++) {
|
||||
searchEndOffset = position.column - 1;
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
const tokenType = lineTokens.getStandardTokenType(tokenIndex);
|
||||
const tokenStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
const tokenEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
|
||||
if (searchStartOffset === 0) {
|
||||
searchStartOffset = tokenStartOffset;
|
||||
}
|
||||
|
||||
if (languageId !== tokenLanguageId) {
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
}
|
||||
if (modeBrackets && !ignoreBracketsInToken(tokenType)) {
|
||||
let r = BracketsUtils.findNextBracketInToken(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, tokenEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
|
||||
let prevSearchInToken = true;
|
||||
for (; tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
|
||||
if (languageId !== tokenLanguageId) {
|
||||
// language id change!
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
prevSearchInToken = false;
|
||||
}
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
}
|
||||
|
||||
const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
|
||||
if (searchInToken) {
|
||||
// this token should be searched
|
||||
if (prevSearchInToken) {
|
||||
// the previous token should be searched, simply extend searchEndOffset
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
} else {
|
||||
// the previous token should not be searched
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
}
|
||||
} else {
|
||||
// this token should not be searched
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
searchStartOffset = 0;
|
||||
prevSearchInToken = searchInToken;
|
||||
}
|
||||
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return this._toFoundBracket(modeBrackets, r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public findEnclosingBrackets(_position: IPosition): [Range, Range] | null {
|
||||
const position = this.validatePosition(_position);
|
||||
const lineCount = this.getLineCount();
|
||||
|
||||
let counts: number[] = [];
|
||||
const resetCounts = (modeBrackets: RichEditBrackets | null) => {
|
||||
counts = [];
|
||||
for (let i = 0, len = modeBrackets ? modeBrackets.brackets.length : 0; i < len; i++) {
|
||||
counts[i] = 0;
|
||||
}
|
||||
};
|
||||
const searchInRange = (modeBrackets: RichEditBrackets, lineNumber: number, lineText: string, searchStartOffset: number, searchEndOffset: number): [Range, Range] | null => {
|
||||
while (true) {
|
||||
const r = BracketsUtils.findNextBracketInRange(modeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (!r) {
|
||||
break;
|
||||
}
|
||||
|
||||
const hitText = lineText.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase();
|
||||
const bracket = modeBrackets.textIsBracket[hitText];
|
||||
if (bracket) {
|
||||
if (bracket.isOpen(hitText)) {
|
||||
counts[bracket.index]++;
|
||||
} else if (bracket.isClose(hitText)) {
|
||||
counts[bracket.index]--;
|
||||
}
|
||||
|
||||
if (counts[bracket.index] === -1) {
|
||||
return this._matchFoundBracket(r, bracket, false);
|
||||
}
|
||||
}
|
||||
|
||||
searchStartOffset = r.endColumn - 1;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
let languageId: LanguageId = -1;
|
||||
let modeBrackets: RichEditBrackets | null = null;
|
||||
for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
|
||||
let tokenIndex = 0;
|
||||
let searchStartOffset = 0;
|
||||
let searchEndOffset = 0;
|
||||
if (lineNumber === position.lineNumber) {
|
||||
tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1);
|
||||
searchStartOffset = position.column - 1;
|
||||
searchEndOffset = position.column - 1;
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
if (languageId !== tokenLanguageId) {
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
resetCounts(modeBrackets);
|
||||
}
|
||||
}
|
||||
|
||||
let prevSearchInToken = true;
|
||||
for (; tokenIndex < tokenCount; tokenIndex++) {
|
||||
const tokenLanguageId = lineTokens.getLanguageId(tokenIndex);
|
||||
|
||||
if (languageId !== tokenLanguageId) {
|
||||
// language id change!
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
prevSearchInToken = false;
|
||||
}
|
||||
languageId = tokenLanguageId;
|
||||
modeBrackets = LanguageConfigurationRegistry.getBracketsSupport(languageId);
|
||||
resetCounts(modeBrackets);
|
||||
}
|
||||
|
||||
const searchInToken = (!!modeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex)));
|
||||
if (searchInToken) {
|
||||
// this token should be searched
|
||||
if (prevSearchInToken) {
|
||||
// the previous token should be searched, simply extend searchEndOffset
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
} else {
|
||||
// the previous token should not be searched
|
||||
searchStartOffset = lineTokens.getStartOffset(tokenIndex);
|
||||
searchEndOffset = lineTokens.getEndOffset(tokenIndex);
|
||||
}
|
||||
} else {
|
||||
// this token should not be searched
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prevSearchInToken = searchInToken;
|
||||
}
|
||||
|
||||
if (modeBrackets && prevSearchInToken && searchStartOffset !== searchEndOffset) {
|
||||
const r = searchInRange(modeBrackets, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ export function isMultilineRegexSource(searchString: string): boolean {
|
||||
}
|
||||
|
||||
const nextChCode = searchString.charCodeAt(i);
|
||||
if (nextChCode === CharCode.n || nextChCode === CharCode.r || nextChCode === CharCode.W) {
|
||||
if (nextChCode === CharCode.n || nextChCode === CharCode.r || nextChCode === CharCode.W || nextChCode === CharCode.w) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { MultilineTokensBuilder, countEOL } from 'vs/editor/common/model/tokensStore';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
const enum Constants {
|
||||
CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048
|
||||
@@ -196,14 +197,14 @@ export class TextModelTokenization extends Disposable {
|
||||
|
||||
private readonly _textModel: TextModel;
|
||||
private readonly _tokenizationStateStore: TokenizationStateStore;
|
||||
private _revalidateTokensTimeout: any;
|
||||
private _isDisposed: boolean;
|
||||
private _tokenizationSupport: ITokenizationSupport | null;
|
||||
|
||||
constructor(textModel: TextModel) {
|
||||
super();
|
||||
this._isDisposed = false;
|
||||
this._textModel = textModel;
|
||||
this._tokenizationStateStore = new TokenizationStateStore();
|
||||
this._revalidateTokensTimeout = -1;
|
||||
this._tokenizationSupport = null;
|
||||
|
||||
this._register(TokenizationRegistry.onDidChange((e) => {
|
||||
@@ -246,19 +247,11 @@ export class TextModelTokenization extends Disposable {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._clearTimers();
|
||||
this._isDisposed = true;
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _clearTimers(): void {
|
||||
if (this._revalidateTokensTimeout !== -1) {
|
||||
clearTimeout(this._revalidateTokensTimeout);
|
||||
this._revalidateTokensTimeout = -1;
|
||||
}
|
||||
}
|
||||
|
||||
private _resetTokenizationState(): void {
|
||||
this._clearTimers();
|
||||
const [tokenizationSupport, initialState] = initializeTokenization(this._textModel);
|
||||
this._tokenizationSupport = tokenizationSupport;
|
||||
this._tokenizationStateStore.flush(initialState);
|
||||
@@ -266,16 +259,19 @@ export class TextModelTokenization extends Disposable {
|
||||
}
|
||||
|
||||
private _beginBackgroundTokenization(): void {
|
||||
if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize() && this._revalidateTokensTimeout === -1) {
|
||||
this._revalidateTokensTimeout = setTimeout(() => {
|
||||
this._revalidateTokensTimeout = -1;
|
||||
if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize()) {
|
||||
platform.setImmediate(() => {
|
||||
if (this._isDisposed) {
|
||||
// disposed in the meantime
|
||||
return;
|
||||
}
|
||||
this._revalidateTokensNow();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _revalidateTokensNow(toLineNumber: number = this._textModel.getLineCount()): void {
|
||||
const MAX_ALLOWED_TIME = 20;
|
||||
const MAX_ALLOWED_TIME = 1;
|
||||
const builder = new MultilineTokensBuilder();
|
||||
const sw = StopWatch.create(false);
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureR
|
||||
import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { keys } from 'vs/base/common/map';
|
||||
|
||||
/**
|
||||
* Open ended enum at runtime
|
||||
@@ -874,40 +875,88 @@ export const enum SymbolTag {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const symbolKindToCssClass = (function () {
|
||||
export namespace SymbolKinds {
|
||||
|
||||
const _fromMapping: { [n: number]: string } = Object.create(null);
|
||||
_fromMapping[SymbolKind.File] = 'file';
|
||||
_fromMapping[SymbolKind.Module] = 'module';
|
||||
_fromMapping[SymbolKind.Namespace] = 'namespace';
|
||||
_fromMapping[SymbolKind.Package] = 'package';
|
||||
_fromMapping[SymbolKind.Class] = 'class';
|
||||
_fromMapping[SymbolKind.Method] = 'method';
|
||||
_fromMapping[SymbolKind.Property] = 'property';
|
||||
_fromMapping[SymbolKind.Field] = 'field';
|
||||
_fromMapping[SymbolKind.Constructor] = 'constructor';
|
||||
_fromMapping[SymbolKind.Enum] = 'enum';
|
||||
_fromMapping[SymbolKind.Interface] = 'interface';
|
||||
_fromMapping[SymbolKind.Function] = 'function';
|
||||
_fromMapping[SymbolKind.Variable] = 'variable';
|
||||
_fromMapping[SymbolKind.Constant] = 'constant';
|
||||
_fromMapping[SymbolKind.String] = 'string';
|
||||
_fromMapping[SymbolKind.Number] = 'number';
|
||||
_fromMapping[SymbolKind.Boolean] = 'boolean';
|
||||
_fromMapping[SymbolKind.Array] = 'array';
|
||||
_fromMapping[SymbolKind.Object] = 'object';
|
||||
_fromMapping[SymbolKind.Key] = 'key';
|
||||
_fromMapping[SymbolKind.Null] = 'null';
|
||||
_fromMapping[SymbolKind.EnumMember] = 'enum-member';
|
||||
_fromMapping[SymbolKind.Struct] = 'struct';
|
||||
_fromMapping[SymbolKind.Event] = 'event';
|
||||
_fromMapping[SymbolKind.Operator] = 'operator';
|
||||
_fromMapping[SymbolKind.TypeParameter] = 'type-parameter';
|
||||
const byName = new Map<string, SymbolKind>();
|
||||
byName.set('file', SymbolKind.File);
|
||||
byName.set('module', SymbolKind.Module);
|
||||
byName.set('namespace', SymbolKind.Namespace);
|
||||
byName.set('package', SymbolKind.Package);
|
||||
byName.set('class', SymbolKind.Class);
|
||||
byName.set('method', SymbolKind.Method);
|
||||
byName.set('property', SymbolKind.Property);
|
||||
byName.set('field', SymbolKind.Field);
|
||||
byName.set('constructor', SymbolKind.Constructor);
|
||||
byName.set('enum', SymbolKind.Enum);
|
||||
byName.set('interface', SymbolKind.Interface);
|
||||
byName.set('function', SymbolKind.Function);
|
||||
byName.set('variable', SymbolKind.Variable);
|
||||
byName.set('constant', SymbolKind.Constant);
|
||||
byName.set('string', SymbolKind.String);
|
||||
byName.set('number', SymbolKind.Number);
|
||||
byName.set('boolean', SymbolKind.Boolean);
|
||||
byName.set('array', SymbolKind.Array);
|
||||
byName.set('object', SymbolKind.Object);
|
||||
byName.set('key', SymbolKind.Key);
|
||||
byName.set('null', SymbolKind.Null);
|
||||
byName.set('enum-member', SymbolKind.EnumMember);
|
||||
byName.set('struct', SymbolKind.Struct);
|
||||
byName.set('event', SymbolKind.Event);
|
||||
byName.set('operator', SymbolKind.Operator);
|
||||
byName.set('type-parameter', SymbolKind.TypeParameter);
|
||||
|
||||
return function toCssClassName(kind: SymbolKind, inline?: boolean): string {
|
||||
return `symbol-icon ${inline ? 'inline' : 'block'} ${_fromMapping[kind] || 'property'}`;
|
||||
};
|
||||
})();
|
||||
const byKind = new Map<SymbolKind, string>();
|
||||
byKind.set(SymbolKind.File, 'file');
|
||||
byKind.set(SymbolKind.Module, 'module');
|
||||
byKind.set(SymbolKind.Namespace, 'namespace');
|
||||
byKind.set(SymbolKind.Package, 'package');
|
||||
byKind.set(SymbolKind.Class, 'class');
|
||||
byKind.set(SymbolKind.Method, 'method');
|
||||
byKind.set(SymbolKind.Property, 'property');
|
||||
byKind.set(SymbolKind.Field, 'field');
|
||||
byKind.set(SymbolKind.Constructor, 'constructor');
|
||||
byKind.set(SymbolKind.Enum, 'enum');
|
||||
byKind.set(SymbolKind.Interface, 'interface');
|
||||
byKind.set(SymbolKind.Function, 'function');
|
||||
byKind.set(SymbolKind.Variable, 'variable');
|
||||
byKind.set(SymbolKind.Constant, 'constant');
|
||||
byKind.set(SymbolKind.String, 'string');
|
||||
byKind.set(SymbolKind.Number, 'number');
|
||||
byKind.set(SymbolKind.Boolean, 'boolean');
|
||||
byKind.set(SymbolKind.Array, 'array');
|
||||
byKind.set(SymbolKind.Object, 'object');
|
||||
byKind.set(SymbolKind.Key, 'key');
|
||||
byKind.set(SymbolKind.Null, 'null');
|
||||
byKind.set(SymbolKind.EnumMember, 'enum-member');
|
||||
byKind.set(SymbolKind.Struct, 'struct');
|
||||
byKind.set(SymbolKind.Event, 'event');
|
||||
byKind.set(SymbolKind.Operator, 'operator');
|
||||
byKind.set(SymbolKind.TypeParameter, 'type-parameter');
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function fromString(value: string): SymbolKind | undefined {
|
||||
return byName.get(value);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function names(): readonly string[] {
|
||||
return keys(byName);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function toString(kind: SymbolKind): string | undefined {
|
||||
return byKind.get(kind);
|
||||
}
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function toCssClassName(kind: SymbolKind, inline?: boolean): string {
|
||||
return `symbol-icon ${inline ? 'inline' : 'block'} ${byKind.get(kind) || 'property'}`;
|
||||
}
|
||||
}
|
||||
|
||||
export interface DocumentSymbol {
|
||||
name: string;
|
||||
|
||||
@@ -247,7 +247,7 @@ export class StandardAutoClosingPairConditional {
|
||||
|
||||
if (Array.isArray(source.notIn)) {
|
||||
for (let i = 0, len = source.notIn.length; i < len; i++) {
|
||||
let notIn = source.notIn[i];
|
||||
const notIn: string = source.notIn[i];
|
||||
switch (notIn) {
|
||||
case 'string':
|
||||
this._standardTokenMask |= StandardTokenType.String;
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier';
|
||||
import { Uint8Matrix } from 'vs/editor/common/core/uint';
|
||||
import { ILink } from 'vs/editor/common/modes';
|
||||
|
||||
export interface ILinkComputerTarget {
|
||||
@@ -33,6 +32,32 @@ export const enum State {
|
||||
|
||||
export type Edge = [State, number, State];
|
||||
|
||||
export class Uint8Matrix {
|
||||
|
||||
private readonly _data: Uint8Array;
|
||||
public readonly rows: number;
|
||||
public readonly cols: number;
|
||||
|
||||
constructor(rows: number, cols: number, defaultValue: number) {
|
||||
const data = new Uint8Array(rows * cols);
|
||||
for (let i = 0, len = rows * cols; i < len; i++) {
|
||||
data[i] = defaultValue;
|
||||
}
|
||||
|
||||
this._data = data;
|
||||
this.rows = rows;
|
||||
this.cols = cols;
|
||||
}
|
||||
|
||||
public get(row: number, col: number): number {
|
||||
return this._data[row * this.cols + col];
|
||||
}
|
||||
|
||||
public set(row: number, col: number, value: number): void {
|
||||
this._data[row * this.cols + col] = value;
|
||||
}
|
||||
}
|
||||
|
||||
export class StateMachine {
|
||||
|
||||
private readonly _states: Uint8Matrix;
|
||||
|
||||
@@ -60,5 +60,17 @@ LanguageConfigurationRegistry.register(PLAINTEXT_LANGUAGE_IDENTIFIER, {
|
||||
['(', ')'],
|
||||
['[', ']'],
|
||||
['{', '}'],
|
||||
]
|
||||
],
|
||||
surroundingPairs: [
|
||||
{ open: '{', close: '}' },
|
||||
{ open: '[', close: ']' },
|
||||
{ open: '(', close: ')' },
|
||||
{ open: '<', close: '>' },
|
||||
{ open: '\"', close: '\"' },
|
||||
{ open: '\'', close: '\'' },
|
||||
{ open: '`', close: '`' },
|
||||
],
|
||||
folding: {
|
||||
offSide: true
|
||||
}
|
||||
});
|
||||
|
||||
@@ -28,10 +28,11 @@ export class BracketElectricCharacterSupport {
|
||||
let result: string[] = [];
|
||||
|
||||
if (this._richEditBrackets) {
|
||||
for (let i = 0, len = this._richEditBrackets.brackets.length; i < len; i++) {
|
||||
let bracketPair = this._richEditBrackets.brackets[i];
|
||||
let lastChar = bracketPair.close.charAt(bracketPair.close.length - 1);
|
||||
result.push(lastChar);
|
||||
for (const bracket of this._richEditBrackets.brackets) {
|
||||
for (const close of bracket.close) {
|
||||
const lastChar = close.charAt(close.length - 1);
|
||||
result.push(lastChar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +57,7 @@ export class BracketElectricCharacterSupport {
|
||||
let reversedBracketRegex = this._richEditBrackets.reversedRegex;
|
||||
let text = context.getLineContent().substring(0, column - 1) + character;
|
||||
|
||||
let r = BracketsUtils.findPrevBracketInToken(reversedBracketRegex, 1, text, 0, text.length);
|
||||
let r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length);
|
||||
if (!r) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -8,27 +8,107 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration';
|
||||
|
||||
interface ISimpleInternalBracket {
|
||||
open: string;
|
||||
close: string;
|
||||
interface InternalBracket {
|
||||
open: string[];
|
||||
close: string[];
|
||||
}
|
||||
|
||||
export class RichEditBracket {
|
||||
_richEditBracketBrand: void;
|
||||
|
||||
readonly languageIdentifier: LanguageIdentifier;
|
||||
readonly open: string;
|
||||
readonly close: string;
|
||||
readonly index: number;
|
||||
readonly open: string[];
|
||||
readonly close: string[];
|
||||
readonly forwardRegex: RegExp;
|
||||
readonly reversedRegex: RegExp;
|
||||
private readonly _openSet: Set<string>;
|
||||
private readonly _closeSet: Set<string>;
|
||||
|
||||
constructor(languageIdentifier: LanguageIdentifier, open: string, close: string, forwardRegex: RegExp, reversedRegex: RegExp) {
|
||||
constructor(languageIdentifier: LanguageIdentifier, index: number, open: string[], close: string[], forwardRegex: RegExp, reversedRegex: RegExp) {
|
||||
this.languageIdentifier = languageIdentifier;
|
||||
this.index = index;
|
||||
this.open = open;
|
||||
this.close = close;
|
||||
this.forwardRegex = forwardRegex;
|
||||
this.reversedRegex = reversedRegex;
|
||||
this._openSet = RichEditBracket._toSet(this.open);
|
||||
this._closeSet = RichEditBracket._toSet(this.close);
|
||||
}
|
||||
|
||||
public isOpen(text: string) {
|
||||
return this._openSet.has(text);
|
||||
}
|
||||
|
||||
public isClose(text: string) {
|
||||
return this._closeSet.has(text);
|
||||
}
|
||||
|
||||
private static _toSet(arr: string[]): Set<string> {
|
||||
const result = new Set<string>();
|
||||
for (const element of arr) {
|
||||
result.add(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
function groupFuzzyBrackets(brackets: CharacterPair[]): InternalBracket[] {
|
||||
const N = brackets.length;
|
||||
|
||||
brackets = brackets.map(b => [b[0].toLowerCase(), b[1].toLowerCase()]);
|
||||
|
||||
const group: number[] = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
group[i] = i;
|
||||
}
|
||||
|
||||
const areOverlapping = (a: CharacterPair, b: CharacterPair) => {
|
||||
const [aOpen, aClose] = a;
|
||||
const [bOpen, bClose] = b;
|
||||
return (aOpen === bOpen || aOpen === bClose || aClose === bOpen || aClose === bClose);
|
||||
};
|
||||
|
||||
const mergeGroups = (g1: number, g2: number) => {
|
||||
const newG = Math.min(g1, g2);
|
||||
const oldG = Math.max(g1, g2);
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (group[i] === oldG) {
|
||||
group[i] = newG;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// group together brackets that have the same open or the same close sequence
|
||||
for (let i = 0; i < N; i++) {
|
||||
const a = brackets[i];
|
||||
for (let j = i + 1; j < N; j++) {
|
||||
const b = brackets[j];
|
||||
if (areOverlapping(a, b)) {
|
||||
mergeGroups(group[i], group[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result: InternalBracket[] = [];
|
||||
for (let g = 0; g < N; g++) {
|
||||
let currentOpen: string[] = [];
|
||||
let currentClose: string[] = [];
|
||||
for (let i = 0; i < N; i++) {
|
||||
if (group[i] === g) {
|
||||
const [open, close] = brackets[i];
|
||||
currentOpen.push(open);
|
||||
currentClose.push(close);
|
||||
}
|
||||
}
|
||||
if (currentOpen.length > 0) {
|
||||
result.push({
|
||||
open: currentOpen,
|
||||
close: currentClose
|
||||
});
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export class RichEditBrackets {
|
||||
@@ -41,87 +121,140 @@ export class RichEditBrackets {
|
||||
public readonly textIsBracket: { [text: string]: RichEditBracket; };
|
||||
public readonly textIsOpenBracket: { [text: string]: boolean; };
|
||||
|
||||
constructor(languageIdentifier: LanguageIdentifier, brackets: CharacterPair[]) {
|
||||
this.brackets = brackets.map((b) => {
|
||||
constructor(languageIdentifier: LanguageIdentifier, _brackets: CharacterPair[]) {
|
||||
const brackets = groupFuzzyBrackets(_brackets);
|
||||
|
||||
this.brackets = brackets.map((b, index) => {
|
||||
return new RichEditBracket(
|
||||
languageIdentifier,
|
||||
b[0],
|
||||
b[1],
|
||||
getRegexForBracketPair({ open: b[0], close: b[1] }),
|
||||
getReversedRegexForBracketPair({ open: b[0], close: b[1] })
|
||||
index,
|
||||
b.open,
|
||||
b.close,
|
||||
getRegexForBracketPair(b.open, b.close, brackets, index),
|
||||
getReversedRegexForBracketPair(b.open, b.close, brackets, index)
|
||||
);
|
||||
});
|
||||
|
||||
this.forwardRegex = getRegexForBrackets(this.brackets);
|
||||
this.reversedRegex = getReversedRegexForBrackets(this.brackets);
|
||||
|
||||
this.textIsBracket = {};
|
||||
this.textIsOpenBracket = {};
|
||||
|
||||
let maxBracketLength = 0;
|
||||
this.brackets.forEach((b) => {
|
||||
this.textIsBracket[b.open.toLowerCase()] = b;
|
||||
this.textIsBracket[b.close.toLowerCase()] = b;
|
||||
this.textIsOpenBracket[b.open.toLowerCase()] = true;
|
||||
this.textIsOpenBracket[b.close.toLowerCase()] = false;
|
||||
maxBracketLength = Math.max(maxBracketLength, b.open.length);
|
||||
maxBracketLength = Math.max(maxBracketLength, b.close.length);
|
||||
});
|
||||
this.maxBracketLength = maxBracketLength;
|
||||
}
|
||||
}
|
||||
|
||||
function once<T, R>(keyFn: (input: T) => string, computeFn: (input: T) => R): (input: T) => R {
|
||||
let cache: { [key: string]: R; } = {};
|
||||
return (input: T): R => {
|
||||
let key = keyFn(input);
|
||||
if (!cache.hasOwnProperty(key)) {
|
||||
cache[key] = computeFn(input);
|
||||
this.maxBracketLength = 0;
|
||||
for (const bracket of this.brackets) {
|
||||
for (const open of bracket.open) {
|
||||
this.textIsBracket[open] = bracket;
|
||||
this.textIsOpenBracket[open] = true;
|
||||
this.maxBracketLength = Math.max(this.maxBracketLength, open.length);
|
||||
}
|
||||
for (const close of bracket.close) {
|
||||
this.textIsBracket[close] = bracket;
|
||||
this.textIsOpenBracket[close] = false;
|
||||
this.maxBracketLength = Math.max(this.maxBracketLength, close.length);
|
||||
}
|
||||
}
|
||||
return cache[key];
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const getRegexForBracketPair = once<ISimpleInternalBracket, RegExp>(
|
||||
(input) => `${input.open};${input.close}`,
|
||||
(input) => {
|
||||
return createBracketOrRegExp([input.open, input.close]);
|
||||
function collectSuperstrings(str: string, brackets: InternalBracket[], currentIndex: number, dest: string[]): void {
|
||||
for (let i = 0, len = brackets.length; i < len; i++) {
|
||||
if (i === currentIndex) {
|
||||
continue;
|
||||
}
|
||||
const bracket = brackets[i];
|
||||
for (const open of bracket.open) {
|
||||
if (open.indexOf(str) >= 0) {
|
||||
dest.push(open);
|
||||
}
|
||||
}
|
||||
for (const close of bracket.close) {
|
||||
if (close.indexOf(str) >= 0) {
|
||||
dest.push(close);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const getReversedRegexForBracketPair = once<ISimpleInternalBracket, RegExp>(
|
||||
(input) => `${input.open};${input.close}`,
|
||||
(input) => {
|
||||
return createBracketOrRegExp([toReversedString(input.open), toReversedString(input.close)]);
|
||||
}
|
||||
);
|
||||
function lengthcmp(a: string, b: string) {
|
||||
return a.length - b.length;
|
||||
}
|
||||
|
||||
const getRegexForBrackets = once<ISimpleInternalBracket[], RegExp>(
|
||||
(input) => input.map(b => `${b.open};${b.close}`).join(';'),
|
||||
(input) => {
|
||||
let pieces: string[] = [];
|
||||
input.forEach((b) => {
|
||||
pieces.push(b.open);
|
||||
pieces.push(b.close);
|
||||
});
|
||||
return createBracketOrRegExp(pieces);
|
||||
function unique(arr: string[]): string[] {
|
||||
if (arr.length <= 1) {
|
||||
return arr;
|
||||
}
|
||||
);
|
||||
const result: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
for (const element of arr) {
|
||||
if (seen.has(element)) {
|
||||
continue;
|
||||
}
|
||||
result.push(element);
|
||||
seen.add(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const getReversedRegexForBrackets = once<ISimpleInternalBracket[], RegExp>(
|
||||
(input) => input.map(b => `${b.open};${b.close}`).join(';'),
|
||||
(input) => {
|
||||
let pieces: string[] = [];
|
||||
input.forEach((b) => {
|
||||
pieces.push(toReversedString(b.open));
|
||||
pieces.push(toReversedString(b.close));
|
||||
});
|
||||
return createBracketOrRegExp(pieces);
|
||||
function getRegexForBracketPair(open: string[], close: string[], brackets: InternalBracket[], currentIndex: number): RegExp {
|
||||
// search in all brackets for other brackets that are a superstring of these brackets
|
||||
let pieces: string[] = [];
|
||||
pieces = pieces.concat(open);
|
||||
pieces = pieces.concat(close);
|
||||
for (let i = 0, len = pieces.length; i < len; i++) {
|
||||
collectSuperstrings(pieces[i], brackets, currentIndex, pieces);
|
||||
}
|
||||
);
|
||||
pieces = unique(pieces);
|
||||
pieces.sort(lengthcmp);
|
||||
pieces.reverse();
|
||||
return createBracketOrRegExp(pieces);
|
||||
}
|
||||
|
||||
function getReversedRegexForBracketPair(open: string[], close: string[], brackets: InternalBracket[], currentIndex: number): RegExp {
|
||||
// search in all brackets for other brackets that are a superstring of these brackets
|
||||
let pieces: string[] = [];
|
||||
pieces = pieces.concat(open);
|
||||
pieces = pieces.concat(close);
|
||||
for (let i = 0, len = pieces.length; i < len; i++) {
|
||||
collectSuperstrings(pieces[i], brackets, currentIndex, pieces);
|
||||
}
|
||||
pieces = unique(pieces);
|
||||
pieces.sort(lengthcmp);
|
||||
pieces.reverse();
|
||||
return createBracketOrRegExp(pieces.map(toReversedString));
|
||||
}
|
||||
|
||||
function getRegexForBrackets(brackets: RichEditBracket[]): RegExp {
|
||||
let pieces: string[] = [];
|
||||
for (const bracket of brackets) {
|
||||
for (const open of bracket.open) {
|
||||
pieces.push(open);
|
||||
}
|
||||
for (const close of bracket.close) {
|
||||
pieces.push(close);
|
||||
}
|
||||
}
|
||||
pieces = unique(pieces);
|
||||
return createBracketOrRegExp(pieces);
|
||||
}
|
||||
|
||||
function getReversedRegexForBrackets(brackets: RichEditBracket[]): RegExp {
|
||||
let pieces: string[] = [];
|
||||
for (const bracket of brackets) {
|
||||
for (const open of bracket.open) {
|
||||
pieces.push(open);
|
||||
}
|
||||
for (const close of bracket.close) {
|
||||
pieces.push(close);
|
||||
}
|
||||
}
|
||||
pieces = unique(pieces);
|
||||
return createBracketOrRegExp(pieces.map(toReversedString));
|
||||
}
|
||||
|
||||
function prepareBracketForRegExp(str: string): string {
|
||||
// This bracket pair uses letters like e.g. "begin" - "end"
|
||||
const insertWordBoundaries = (/^[\w]+$/.test(str));
|
||||
const insertWordBoundaries = (/^[\w ]+$/.test(str));
|
||||
str = strings.escapeRegExpCharacters(str);
|
||||
return (insertWordBoundaries ? `\\b${str}\\b` : str);
|
||||
}
|
||||
@@ -168,12 +301,11 @@ export class BracketsUtils {
|
||||
return new Range(lineNumber, absoluteMatchOffset - matchLength + 1, lineNumber, absoluteMatchOffset + 1);
|
||||
}
|
||||
|
||||
public static findPrevBracketInToken(reversedBracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range | null {
|
||||
public static findPrevBracketInRange(reversedBracketRegex: RegExp, lineNumber: number, lineText: string, startOffset: number, endOffset: number): Range | null {
|
||||
// Because JS does not support backwards regex search, we search forwards in a reversed string with a reversed regex ;)
|
||||
let reversedLineText = toReversedString(lineText);
|
||||
let reversedTokenText = reversedLineText.substring(lineText.length - currentTokenEnd, lineText.length - currentTokenStart);
|
||||
|
||||
return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedTokenText, currentTokenStart);
|
||||
const reversedLineText = toReversedString(lineText);
|
||||
const reversedSubstr = reversedLineText.substring(lineText.length - endOffset, lineText.length - startOffset);
|
||||
return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedSubstr, startOffset);
|
||||
}
|
||||
|
||||
public static findNextBracketInText(bracketRegex: RegExp, lineNumber: number, text: string, offset: number): Range | null {
|
||||
@@ -193,10 +325,8 @@ export class BracketsUtils {
|
||||
return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength);
|
||||
}
|
||||
|
||||
public static findNextBracketInToken(bracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range | null {
|
||||
let currentTokenText = lineText.substring(currentTokenStart, currentTokenEnd);
|
||||
|
||||
return this.findNextBracketInText(bracketRegex, lineNumber, currentTokenText, currentTokenStart);
|
||||
public static findNextBracketInRange(bracketRegex: RegExp, lineNumber: number, lineText: string, startOffset: number, endOffset: number): Range | null {
|
||||
const substr = lineText.substring(startOffset, endOffset);
|
||||
return this.findNextBracketInText(bracketRegex, lineNumber, substr, startOffset);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function tokenizeToString(text: string, tokenizationSupport: IReducedToke
|
||||
return _tokenizeToString(text, tokenizationSupport || fallback);
|
||||
}
|
||||
|
||||
export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens, colorMap: string[], startOffset: number, endOffset: number, tabSize: number): string {
|
||||
export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens, colorMap: string[], startOffset: number, endOffset: number, tabSize: number, useNbsp: boolean): string {
|
||||
let result = `<div>`;
|
||||
let charIndex = startOffset;
|
||||
let tabsCharDelta = 0;
|
||||
@@ -46,7 +46,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens
|
||||
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
|
||||
tabsCharDelta += insertSpacesCount - 1;
|
||||
while (insertSpacesCount > 0) {
|
||||
partContent += ' ';
|
||||
partContent += useNbsp ? ' ' : ' ';
|
||||
insertSpacesCount--;
|
||||
}
|
||||
break;
|
||||
@@ -78,7 +78,7 @@ export function tokenizeLineToHTML(text: string, viewLineTokens: IViewLineTokens
|
||||
break;
|
||||
|
||||
case CharCode.Space:
|
||||
partContent += ' ';
|
||||
partContent += useNbsp ? ' ' : ' ';
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
@@ -377,11 +377,11 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
// ---- BEGIN diff --------------------------------------------------------------------------
|
||||
|
||||
public computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
|
||||
public async computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
const original = this._getModel(originalUrl);
|
||||
const modified = this._getModel(modifiedUrl);
|
||||
if (!original || !modified) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
const originalLines = original.getLinesContent();
|
||||
@@ -390,15 +390,17 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
shouldComputeCharChanges: true,
|
||||
shouldPostProcessCharChanges: true,
|
||||
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
|
||||
shouldMakePrettyDiff: true
|
||||
shouldMakePrettyDiff: true,
|
||||
maxComputationTime: maxComputationTime
|
||||
});
|
||||
|
||||
const changes = diffComputer.computeDiff();
|
||||
let identical = (changes.length > 0 ? false : this._modelsAreIdentical(original, modified));
|
||||
return Promise.resolve({
|
||||
const diffResult = diffComputer.computeDiff();
|
||||
const identical = (diffResult.changes.length > 0 ? false : this._modelsAreIdentical(original, modified));
|
||||
return {
|
||||
quitEarly: diffResult.quitEarly,
|
||||
identical: identical,
|
||||
changes: changes
|
||||
});
|
||||
changes: diffResult.changes
|
||||
};
|
||||
}
|
||||
|
||||
private _modelsAreIdentical(original: ICommonModel, modified: ICommonModel): boolean {
|
||||
@@ -417,11 +419,11 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
return true;
|
||||
}
|
||||
|
||||
public computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise<editorCommon.IChange[] | null> {
|
||||
public async computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise<editorCommon.IChange[] | null> {
|
||||
let original = this._getModel(originalUrl);
|
||||
let modified = this._getModel(modifiedUrl);
|
||||
if (!original || !modified) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
let originalLines = original.getLinesContent();
|
||||
@@ -430,9 +432,10 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
shouldComputeCharChanges: false,
|
||||
shouldPostProcessCharChanges: false,
|
||||
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
|
||||
shouldMakePrettyDiff: true
|
||||
shouldMakePrettyDiff: true,
|
||||
maxComputationTime: 1000
|
||||
});
|
||||
return Promise.resolve(diffComputer.computeDiff());
|
||||
return diffComputer.computeDiff().changes;
|
||||
}
|
||||
|
||||
// ---- END diff --------------------------------------------------------------------------
|
||||
@@ -442,10 +445,10 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
private static readonly _diffLimit = 100000;
|
||||
|
||||
public computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[]): Promise<TextEdit[]> {
|
||||
public async computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[]): Promise<TextEdit[]> {
|
||||
const model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return Promise.resolve(edits);
|
||||
return edits;
|
||||
}
|
||||
|
||||
const result: TextEdit[] = [];
|
||||
@@ -508,28 +511,28 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
result.push({ eol: lastEol, text: '', range: { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 } });
|
||||
}
|
||||
|
||||
return Promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---- END minimal edits ---------------------------------------------------------------
|
||||
|
||||
public computeLinks(modelUrl: string): Promise<ILink[] | null> {
|
||||
public async computeLinks(modelUrl: string): Promise<ILink[] | null> {
|
||||
let model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
return Promise.resolve(computeLinks(model));
|
||||
return computeLinks(model);
|
||||
}
|
||||
|
||||
// ---- BEGIN suggest --------------------------------------------------------------------------
|
||||
|
||||
private static readonly _suggestionsLimit = 10000;
|
||||
|
||||
public textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<CompletionList | null> {
|
||||
public async textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<CompletionList | null> {
|
||||
const model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
const seen: Record<string, boolean> = Object.create(null);
|
||||
@@ -563,7 +566,7 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
range: { startLineNumber: position.lineNumber, startColumn: wordUntil.startColumn, endLineNumber: position.lineNumber, endColumn: wordUntil.endColumn }
|
||||
});
|
||||
}
|
||||
return Promise.resolve({ suggestions });
|
||||
return { suggestions };
|
||||
}
|
||||
|
||||
|
||||
@@ -571,10 +574,10 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
//#region -- word ranges --
|
||||
|
||||
computeWordRanges(modelUrl: string, range: IRange, wordDef: string, wordDefFlags: string): Promise<{ [word: string]: IRange[] }> {
|
||||
public async computeWordRanges(modelUrl: string, range: IRange, wordDef: string, wordDefFlags: string): Promise<{ [word: string]: IRange[] }> {
|
||||
let model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return Promise.resolve(Object.create(null));
|
||||
return Object.create(null);
|
||||
}
|
||||
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
|
||||
const result: { [word: string]: IRange[] } = Object.create(null);
|
||||
@@ -597,15 +600,15 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
});
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
public navigateValueSet(modelUrl: string, range: IRange, up: boolean, wordDef: string, wordDefFlags: string): Promise<IInplaceReplaceSupportResult | null> {
|
||||
public async navigateValueSet(modelUrl: string, range: IRange, up: boolean, wordDef: string, wordDefFlags: string): Promise<IInplaceReplaceSupportResult | null> {
|
||||
let model = this._getModel(modelUrl);
|
||||
if (!model) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
|
||||
let wordDefRegExp = new RegExp(wordDef, wordDefFlags);
|
||||
@@ -623,11 +626,11 @@ export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
|
||||
let wordRange = model.getWordAtPosition({ lineNumber: range.startLineNumber, column: range.startColumn }, wordDefRegExp);
|
||||
if (!wordRange) {
|
||||
return Promise.resolve(null);
|
||||
return null;
|
||||
}
|
||||
let word = model.getValueInRange(wordRange);
|
||||
let result = BasicInplaceReplace.INSTANCE.navigateValueSet(range, selectionText, wordRange, word, up);
|
||||
return Promise.resolve(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---- BEGIN foreign module support --------------------------------------------------------------------------
|
||||
|
||||
@@ -13,6 +13,7 @@ export const ID_EDITOR_WORKER_SERVICE = 'editorWorkerService';
|
||||
export const IEditorWorkerService = createDecorator<IEditorWorkerService>(ID_EDITOR_WORKER_SERVICE);
|
||||
|
||||
export interface IDiffComputationResult {
|
||||
quitEarly: boolean;
|
||||
identical: boolean;
|
||||
changes: ILineChange[];
|
||||
}
|
||||
@@ -21,7 +22,7 @@ export interface IEditorWorkerService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
canComputeDiff(original: URI, modified: URI): boolean;
|
||||
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null>;
|
||||
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null>;
|
||||
|
||||
canComputeDirtyDiff(original: URI, modified: URI): boolean;
|
||||
computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null>;
|
||||
|
||||
@@ -82,8 +82,8 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
|
||||
return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified));
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
|
||||
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace));
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace, maxComputationTime));
|
||||
}
|
||||
|
||||
public canComputeDirtyDiff(original: URI, modified: URI): boolean {
|
||||
@@ -409,9 +409,9 @@ export class EditorWorkerClient extends Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
|
||||
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean, maxComputationTime: number): Promise<IDiffComputationResult | null> {
|
||||
return this._withSyncedResources([original, modified]).then(proxy => {
|
||||
return proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
|
||||
return proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace, maxComputationTime);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -218,6 +218,10 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
||||
}
|
||||
|
||||
private static _setModelOptionsForModel(model: ITextModel, newOptions: ITextModelCreationOptions, currentOptions: ITextModelCreationOptions): void {
|
||||
if (currentOptions && currentOptions.defaultEOL !== newOptions.defaultEOL && model.getLineCount() === 1) {
|
||||
model.setEOL(newOptions.defaultEOL === DefaultEndOfLine.LF ? EndOfLineSequence.LF : EndOfLineSequence.CRLF);
|
||||
}
|
||||
|
||||
if (currentOptions
|
||||
&& (currentOptions.detectIndentation === newOptions.detectIndentation)
|
||||
&& (currentOptions.insertSpaces === newOptions.insertSpaces)
|
||||
|
||||
@@ -403,10 +403,8 @@ export enum TextEditorCursorStyle {
|
||||
|
||||
export enum RenderMinimap {
|
||||
None = 0,
|
||||
Small = 1,
|
||||
Large = 2,
|
||||
SmallBlocks = 3,
|
||||
LargeBlocks = 4
|
||||
Text = 1,
|
||||
Blocks = 2
|
||||
}
|
||||
|
||||
export enum RenderLineNumbersType {
|
||||
|
||||
@@ -1,349 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { RGBA8 } from 'vs/editor/common/core/rgba';
|
||||
import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
|
||||
export class MinimapTokensColorTracker {
|
||||
private static _INSTANCE: MinimapTokensColorTracker | null = null;
|
||||
public static getInstance(): MinimapTokensColorTracker {
|
||||
if (!this._INSTANCE) {
|
||||
this._INSTANCE = new MinimapTokensColorTracker();
|
||||
}
|
||||
return this._INSTANCE;
|
||||
}
|
||||
|
||||
private _colors!: RGBA8[];
|
||||
private _backgroundIsLight!: boolean;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
public readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private constructor() {
|
||||
this._updateColorMap();
|
||||
TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.changedColorMap) {
|
||||
this._updateColorMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _updateColorMap(): void {
|
||||
const colorMap = TokenizationRegistry.getColorMap();
|
||||
if (!colorMap) {
|
||||
this._colors = [RGBA8.Empty];
|
||||
this._backgroundIsLight = true;
|
||||
return;
|
||||
}
|
||||
this._colors = [RGBA8.Empty];
|
||||
for (let colorId = 1; colorId < colorMap.length; colorId++) {
|
||||
const source = colorMap[colorId].rgba;
|
||||
// Use a VM friendly data-type
|
||||
this._colors[colorId] = new RGBA8(source.r, source.g, source.b, Math.round(source.a * 255));
|
||||
}
|
||||
let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance();
|
||||
this._backgroundIsLight = (backgroundLuminosity >= 0.5);
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
|
||||
public getColor(colorId: ColorId): RGBA8 {
|
||||
if (colorId < 1 || colorId >= this._colors.length) {
|
||||
// background color (basically invisible)
|
||||
colorId = ColorId.DefaultBackground;
|
||||
}
|
||||
return this._colors[colorId];
|
||||
}
|
||||
|
||||
public backgroundIsLight(): boolean {
|
||||
return this._backgroundIsLight;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Constants {
|
||||
START_CH_CODE = 32, // Space
|
||||
END_CH_CODE = 126, // Tilde (~)
|
||||
CHAR_COUNT = END_CH_CODE - START_CH_CODE + 1,
|
||||
|
||||
SAMPLED_CHAR_HEIGHT = 16,
|
||||
SAMPLED_CHAR_WIDTH = 10,
|
||||
SAMPLED_HALF_CHAR_WIDTH = SAMPLED_CHAR_WIDTH / 2,
|
||||
|
||||
x2_CHAR_HEIGHT = 4,
|
||||
x2_CHAR_WIDTH = 2,
|
||||
|
||||
x1_CHAR_HEIGHT = 2,
|
||||
x1_CHAR_WIDTH = 1,
|
||||
|
||||
RGBA_CHANNELS_CNT = 4,
|
||||
}
|
||||
|
||||
export class MinimapCharRenderer {
|
||||
|
||||
_minimapCharRendererBrand: void;
|
||||
|
||||
public readonly x2charData: Uint8ClampedArray;
|
||||
public readonly x1charData: Uint8ClampedArray;
|
||||
|
||||
public readonly x2charDataLight: Uint8ClampedArray;
|
||||
public readonly x1charDataLight: Uint8ClampedArray;
|
||||
|
||||
constructor(x2CharData: Uint8ClampedArray, x1CharData: Uint8ClampedArray) {
|
||||
const x2ExpectedLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT;
|
||||
if (x2CharData.length !== x2ExpectedLen) {
|
||||
throw new Error('Invalid x2CharData');
|
||||
}
|
||||
const x1ExpectedLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT;
|
||||
if (x1CharData.length !== x1ExpectedLen) {
|
||||
throw new Error('Invalid x1CharData');
|
||||
}
|
||||
this.x2charData = x2CharData;
|
||||
this.x1charData = x1CharData;
|
||||
|
||||
this.x2charDataLight = MinimapCharRenderer.soften(x2CharData, 12 / 15);
|
||||
this.x1charDataLight = MinimapCharRenderer.soften(x1CharData, 50 / 60);
|
||||
}
|
||||
|
||||
private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray {
|
||||
let result = new Uint8ClampedArray(input.length);
|
||||
for (let i = 0, len = input.length; i < len; i++) {
|
||||
result[i] = input[i] * ratio;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _getChIndex(chCode: number): number {
|
||||
chCode -= Constants.START_CH_CODE;
|
||||
if (chCode < 0) {
|
||||
chCode += Constants.CHAR_COUNT;
|
||||
}
|
||||
return (chCode % Constants.CHAR_COUNT);
|
||||
}
|
||||
|
||||
public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void {
|
||||
if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
const x2CharData = useLighterFont ? this.x2charDataLight : this.x2charData;
|
||||
const chIndex = MinimapCharRenderer._getChIndex(chCode);
|
||||
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const dest = target.data;
|
||||
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
const c = x2CharData[sourceOffset] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 1] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 2] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 3] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 4] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 5] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 6] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 7] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
}
|
||||
|
||||
public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void {
|
||||
if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
const x1CharData = useLighterFont ? this.x1charDataLight : this.x1charData;
|
||||
const chIndex = MinimapCharRenderer._getChIndex(chCode);
|
||||
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const dest = target.data;
|
||||
const sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH;
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
const c = x1CharData[sourceOffset] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x1CharData[sourceOffset + 1] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
}
|
||||
|
||||
public x2BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void {
|
||||
if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const c = 0.5;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const colorR = backgroundR + deltaR * c;
|
||||
const colorG = backgroundG + deltaG * c;
|
||||
const colorB = backgroundB + deltaB * c;
|
||||
|
||||
const dest = target.data;
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
{
|
||||
dest[destOffset + 4] = colorR;
|
||||
dest[destOffset + 5] = colorG;
|
||||
dest[destOffset + 6] = colorB;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
{
|
||||
dest[destOffset + 4] = colorR;
|
||||
dest[destOffset + 5] = colorG;
|
||||
dest[destOffset + 6] = colorB;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
{
|
||||
dest[destOffset + 4] = colorR;
|
||||
dest[destOffset + 5] = colorG;
|
||||
dest[destOffset + 6] = colorB;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
{
|
||||
dest[destOffset + 4] = colorR;
|
||||
dest[destOffset + 5] = colorG;
|
||||
dest[destOffset + 6] = colorB;
|
||||
}
|
||||
}
|
||||
|
||||
public x1BlockRenderChar(target: ImageData, dx: number, dy: number, color: RGBA8, backgroundColor: RGBA8, useLighterFont: boolean): void {
|
||||
if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const c = 0.5;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const colorR = backgroundR + deltaR * c;
|
||||
const colorG = backgroundG + deltaG * c;
|
||||
const colorB = backgroundB + deltaB * c;
|
||||
|
||||
const dest = target.data;
|
||||
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
dest[destOffset + 0] = colorR;
|
||||
dest[destOffset + 1] = colorG;
|
||||
dest[destOffset + 2] = colorB;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,985 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
|
||||
function toUint8ClampedArrat(arr: number[]): Uint8ClampedArray {
|
||||
let r = new Uint8ClampedArray(arr.length);
|
||||
for (let i = 0, len = arr.length; i < len; i++) {
|
||||
r[i] = arr[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
let minimapCharRenderer: MinimapCharRenderer | null = null;
|
||||
export function getOrCreateMinimapCharRenderer(): MinimapCharRenderer {
|
||||
if (!minimapCharRenderer) {
|
||||
let _x1Data = toUint8ClampedArrat(x1Data!);
|
||||
x1Data = null;
|
||||
|
||||
let _x2Data = toUint8ClampedArrat(x2Data!);
|
||||
x2Data = null;
|
||||
minimapCharRenderer = new MinimapCharRenderer(_x2Data, _x1Data);
|
||||
}
|
||||
return minimapCharRenderer;
|
||||
}
|
||||
|
||||
let x2Data: number[] | null = [
|
||||
|
||||
//
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
// !
|
||||
39, 14,
|
||||
39, 14,
|
||||
14, 5,
|
||||
29, 10,
|
||||
|
||||
// "
|
||||
96, 96,
|
||||
29, 29,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
// #
|
||||
49, 113,
|
||||
195, 214,
|
||||
227, 166,
|
||||
135, 42,
|
||||
|
||||
// $
|
||||
40, 29,
|
||||
194, 38,
|
||||
75, 148,
|
||||
197, 187,
|
||||
|
||||
// %
|
||||
145, 0,
|
||||
160, 61,
|
||||
75, 143,
|
||||
2, 183,
|
||||
|
||||
// &
|
||||
138, 58,
|
||||
163, 6,
|
||||
177, 223,
|
||||
197, 227,
|
||||
|
||||
// '
|
||||
38, 13,
|
||||
11, 4,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
// (
|
||||
10, 54,
|
||||
52, 8,
|
||||
62, 4,
|
||||
71, 122,
|
||||
|
||||
// )
|
||||
73, 2,
|
||||
19, 40,
|
||||
10, 50,
|
||||
155, 36,
|
||||
|
||||
// *
|
||||
79, 70,
|
||||
145, 121,
|
||||
7, 5,
|
||||
0, 0,
|
||||
|
||||
// +
|
||||
2, 1,
|
||||
36, 12,
|
||||
204, 166,
|
||||
16, 5,
|
||||
|
||||
// ,
|
||||
0, 0,
|
||||
0, 0,
|
||||
1, 0,
|
||||
154, 34,
|
||||
|
||||
// -
|
||||
0, 0,
|
||||
0, 0,
|
||||
96, 83,
|
||||
0, 0,
|
||||
|
||||
// .
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
46, 34,
|
||||
|
||||
// /
|
||||
0, 82,
|
||||
2, 56,
|
||||
53, 3,
|
||||
146, 0,
|
||||
|
||||
// 0
|
||||
146, 119,
|
||||
152, 132,
|
||||
152, 131,
|
||||
145, 119,
|
||||
|
||||
// 1
|
||||
170, 42,
|
||||
15, 42,
|
||||
15, 42,
|
||||
172, 194,
|
||||
|
||||
// 2
|
||||
131, 132,
|
||||
0, 139,
|
||||
80, 28,
|
||||
227, 143,
|
||||
|
||||
// 3
|
||||
159, 135,
|
||||
15, 118,
|
||||
11, 126,
|
||||
171, 144,
|
||||
|
||||
// 4
|
||||
20, 124,
|
||||
88, 106,
|
||||
217, 196,
|
||||
0, 106,
|
||||
|
||||
// 5
|
||||
189, 92,
|
||||
168, 43,
|
||||
5, 130,
|
||||
164, 133,
|
||||
|
||||
// 6
|
||||
130, 115,
|
||||
183, 65,
|
||||
134, 120,
|
||||
141, 141,
|
||||
|
||||
// 7
|
||||
170, 196,
|
||||
2, 106,
|
||||
31, 32,
|
||||
105, 2,
|
||||
|
||||
// 8
|
||||
145, 130,
|
||||
116, 114,
|
||||
132, 135,
|
||||
138, 140,
|
||||
|
||||
// 9
|
||||
138, 113,
|
||||
147, 137,
|
||||
81, 183,
|
||||
129, 94,
|
||||
|
||||
// :
|
||||
0, 0,
|
||||
21, 16,
|
||||
4, 3,
|
||||
46, 34,
|
||||
|
||||
// ;
|
||||
0, 0,
|
||||
45, 34,
|
||||
1, 0,
|
||||
160, 49,
|
||||
|
||||
// <
|
||||
0, 0,
|
||||
43, 143,
|
||||
203, 23,
|
||||
1, 76,
|
||||
|
||||
// =
|
||||
0, 0,
|
||||
38, 28,
|
||||
131, 96,
|
||||
38, 28,
|
||||
|
||||
// >
|
||||
0, 0,
|
||||
168, 31,
|
||||
29, 191,
|
||||
98, 0,
|
||||
|
||||
// ?
|
||||
118, 139,
|
||||
5, 113,
|
||||
45, 13,
|
||||
37, 6,
|
||||
|
||||
// @
|
||||
97, 115,
|
||||
161, 179,
|
||||
204, 105,
|
||||
223, 224,
|
||||
|
||||
// A
|
||||
83, 52,
|
||||
111, 100,
|
||||
184, 186,
|
||||
120, 132,
|
||||
|
||||
// B
|
||||
212, 145,
|
||||
180, 139,
|
||||
174, 161,
|
||||
212, 182,
|
||||
|
||||
// C
|
||||
104, 162,
|
||||
131, 0,
|
||||
131, 0,
|
||||
104, 161,
|
||||
|
||||
// D
|
||||
219, 120,
|
||||
110, 116,
|
||||
110, 116,
|
||||
219, 120,
|
||||
|
||||
// E
|
||||
207, 154,
|
||||
163, 40,
|
||||
147, 22,
|
||||
207, 154,
|
||||
|
||||
// F
|
||||
202, 159,
|
||||
161, 47,
|
||||
145, 23,
|
||||
111, 0,
|
||||
|
||||
// G
|
||||
139, 154,
|
||||
144, 30,
|
||||
144, 135,
|
||||
139, 187,
|
||||
|
||||
// H
|
||||
110, 110,
|
||||
168, 161,
|
||||
150, 145,
|
||||
110, 110,
|
||||
|
||||
// I
|
||||
185, 162,
|
||||
43, 16,
|
||||
43, 16,
|
||||
185, 162,
|
||||
|
||||
// J
|
||||
73, 129,
|
||||
0, 110,
|
||||
0, 110,
|
||||
191, 87,
|
||||
|
||||
// K
|
||||
149, 149,
|
||||
236, 48,
|
||||
195, 91,
|
||||
146, 149,
|
||||
|
||||
// L
|
||||
146, 0,
|
||||
146, 0,
|
||||
146, 0,
|
||||
187, 173,
|
||||
|
||||
// M
|
||||
200, 201,
|
||||
222, 215,
|
||||
172, 147,
|
||||
95, 95,
|
||||
|
||||
// N
|
||||
193, 97,
|
||||
224, 129,
|
||||
159, 206,
|
||||
97, 192,
|
||||
|
||||
// O
|
||||
155, 139,
|
||||
153, 115,
|
||||
153, 115,
|
||||
156, 140,
|
||||
|
||||
// P
|
||||
189, 158,
|
||||
123, 136,
|
||||
190, 64,
|
||||
111, 0,
|
||||
|
||||
// Q
|
||||
155, 139,
|
||||
153, 115,
|
||||
153, 114,
|
||||
156, 241,
|
||||
|
||||
// R
|
||||
197, 148,
|
||||
150, 152,
|
||||
170, 116,
|
||||
110, 157,
|
||||
|
||||
// S
|
||||
156, 128,
|
||||
169, 14,
|
||||
13, 159,
|
||||
158, 149,
|
||||
|
||||
// T
|
||||
212, 189,
|
||||
43, 16,
|
||||
43, 16,
|
||||
43, 16,
|
||||
|
||||
// U
|
||||
148, 110,
|
||||
148, 110,
|
||||
147, 109,
|
||||
182, 151,
|
||||
|
||||
// V
|
||||
133, 121,
|
||||
106, 118,
|
||||
114, 103,
|
||||
89, 66,
|
||||
|
||||
// W
|
||||
94, 94,
|
||||
211, 188,
|
||||
205, 207,
|
||||
139, 168,
|
||||
|
||||
// X
|
||||
151, 152,
|
||||
87, 76,
|
||||
101, 79,
|
||||
151, 152,
|
||||
|
||||
// Y
|
||||
130, 156,
|
||||
125, 116,
|
||||
47, 29,
|
||||
43, 16,
|
||||
|
||||
// Z
|
||||
169, 228,
|
||||
11, 103,
|
||||
120, 6,
|
||||
230, 176,
|
||||
|
||||
// [
|
||||
55, 49,
|
||||
55, 6,
|
||||
55, 6,
|
||||
193, 102,
|
||||
|
||||
// \
|
||||
92, 0,
|
||||
71, 0,
|
||||
13, 30,
|
||||
0, 147,
|
||||
|
||||
// ]
|
||||
63, 43,
|
||||
12, 43,
|
||||
12, 43,
|
||||
142, 152,
|
||||
|
||||
// ^
|
||||
71, 53,
|
||||
61, 61,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
// _
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
158, 146,
|
||||
|
||||
// `
|
||||
25, 2,
|
||||
0, 0,
|
||||
0, 0,
|
||||
0, 0,
|
||||
|
||||
// a
|
||||
0, 0,
|
||||
107, 130,
|
||||
170, 194,
|
||||
176, 188,
|
||||
|
||||
// b
|
||||
109, 0,
|
||||
203, 159,
|
||||
113, 111,
|
||||
202, 158,
|
||||
|
||||
// c
|
||||
0, 0,
|
||||
135, 135,
|
||||
114, 0,
|
||||
136, 135,
|
||||
|
||||
// d
|
||||
0, 109,
|
||||
187, 190,
|
||||
148, 126,
|
||||
177, 187,
|
||||
|
||||
// e
|
||||
0, 0,
|
||||
149, 130,
|
||||
218, 105,
|
||||
169, 135,
|
||||
|
||||
// f
|
||||
37, 113,
|
||||
146, 113,
|
||||
49, 13,
|
||||
49, 13,
|
||||
|
||||
// g
|
||||
0, 0,
|
||||
178, 195,
|
||||
147, 114,
|
||||
255, 255,
|
||||
|
||||
// h
|
||||
109, 0,
|
||||
193, 149,
|
||||
110, 109,
|
||||
109, 109,
|
||||
|
||||
// i
|
||||
12, 15,
|
||||
125, 41,
|
||||
33, 41,
|
||||
144, 188,
|
||||
|
||||
// j
|
||||
1, 6,
|
||||
75, 53,
|
||||
10, 53,
|
||||
210, 161,
|
||||
|
||||
// k
|
||||
110, 0,
|
||||
152, 148,
|
||||
210, 60,
|
||||
110, 156,
|
||||
|
||||
// l
|
||||
213, 5,
|
||||
63, 5,
|
||||
63, 5,
|
||||
45, 111,
|
||||
|
||||
// m
|
||||
0, 0,
|
||||
232, 172,
|
||||
190, 168,
|
||||
190, 169,
|
||||
|
||||
// n
|
||||
0, 0,
|
||||
190, 144,
|
||||
109, 109,
|
||||
109, 109,
|
||||
|
||||
// o
|
||||
0, 0,
|
||||
168, 140,
|
||||
148, 111,
|
||||
168, 140,
|
||||
|
||||
// p
|
||||
0, 0,
|
||||
200, 151,
|
||||
113, 110,
|
||||
255, 158,
|
||||
|
||||
// q
|
||||
0, 0,
|
||||
184, 188,
|
||||
147, 139,
|
||||
186, 255,
|
||||
|
||||
// r
|
||||
0, 0,
|
||||
122, 130,
|
||||
111, 0,
|
||||
109, 0,
|
||||
|
||||
// s
|
||||
0, 0,
|
||||
132, 69,
|
||||
109, 93,
|
||||
110, 136,
|
||||
|
||||
// t
|
||||
51, 5,
|
||||
205, 103,
|
||||
61, 6,
|
||||
47, 106,
|
||||
|
||||
// u
|
||||
0, 0,
|
||||
110, 109,
|
||||
110, 122,
|
||||
155, 179,
|
||||
|
||||
// v
|
||||
0, 0,
|
||||
132, 120,
|
||||
113, 114,
|
||||
84, 63,
|
||||
|
||||
// w
|
||||
0, 0,
|
||||
124, 108,
|
||||
202, 189,
|
||||
160, 174,
|
||||
|
||||
// x
|
||||
0, 0,
|
||||
144, 142,
|
||||
79, 57,
|
||||
159, 146,
|
||||
|
||||
// y
|
||||
0, 0,
|
||||
138, 138,
|
||||
119, 117,
|
||||
255, 69,
|
||||
|
||||
// z
|
||||
0, 0,
|
||||
97, 198,
|
||||
47, 38,
|
||||
208, 84,
|
||||
|
||||
// {
|
||||
23, 112,
|
||||
41, 14,
|
||||
157, 7,
|
||||
121, 192,
|
||||
|
||||
// |
|
||||
35, 11,
|
||||
35, 11,
|
||||
35, 11,
|
||||
160, 61,
|
||||
|
||||
// }
|
||||
129, 9,
|
||||
40, 19,
|
||||
20, 139,
|
||||
236, 44,
|
||||
|
||||
// ~
|
||||
0, 0,
|
||||
15, 3,
|
||||
97, 93,
|
||||
0, 0,
|
||||
|
||||
];
|
||||
|
||||
let x1Data: number[] | null = [
|
||||
|
||||
//
|
||||
0,
|
||||
0,
|
||||
|
||||
// !
|
||||
23,
|
||||
12,
|
||||
|
||||
// "
|
||||
53,
|
||||
0,
|
||||
|
||||
// #
|
||||
130,
|
||||
127,
|
||||
|
||||
// $
|
||||
58,
|
||||
149,
|
||||
|
||||
// %
|
||||
67,
|
||||
77,
|
||||
|
||||
// &
|
||||
72,
|
||||
198,
|
||||
|
||||
// '
|
||||
13,
|
||||
0,
|
||||
|
||||
// (
|
||||
25,
|
||||
51,
|
||||
|
||||
// )
|
||||
25,
|
||||
49,
|
||||
|
||||
// *
|
||||
94,
|
||||
2,
|
||||
|
||||
// +
|
||||
8,
|
||||
64,
|
||||
|
||||
// ,
|
||||
0,
|
||||
24,
|
||||
|
||||
// -
|
||||
0,
|
||||
21,
|
||||
|
||||
// .
|
||||
0,
|
||||
9,
|
||||
|
||||
// /
|
||||
19,
|
||||
27,
|
||||
|
||||
// 0
|
||||
126,
|
||||
126,
|
||||
|
||||
// 1
|
||||
51,
|
||||
80,
|
||||
|
||||
// 2
|
||||
72,
|
||||
105,
|
||||
|
||||
// 3
|
||||
87,
|
||||
98,
|
||||
|
||||
// 4
|
||||
73,
|
||||
93,
|
||||
|
||||
// 5
|
||||
106,
|
||||
85,
|
||||
|
||||
// 6
|
||||
111,
|
||||
123,
|
||||
|
||||
// 7
|
||||
87,
|
||||
30,
|
||||
|
||||
// 8
|
||||
116,
|
||||
126,
|
||||
|
||||
// 9
|
||||
123,
|
||||
110,
|
||||
|
||||
// :
|
||||
4,
|
||||
16,
|
||||
|
||||
// ;
|
||||
9,
|
||||
28,
|
||||
|
||||
// <
|
||||
21,
|
||||
53,
|
||||
|
||||
// =
|
||||
8,
|
||||
62,
|
||||
|
||||
// >
|
||||
23,
|
||||
52,
|
||||
|
||||
// ?
|
||||
73,
|
||||
21,
|
||||
|
||||
// @
|
||||
132,
|
||||
183,
|
||||
|
||||
// A
|
||||
78,
|
||||
142,
|
||||
|
||||
// B
|
||||
168,
|
||||
175,
|
||||
|
||||
// C
|
||||
70,
|
||||
70,
|
||||
|
||||
// D
|
||||
128,
|
||||
128,
|
||||
|
||||
// E
|
||||
123,
|
||||
110,
|
||||
|
||||
// F
|
||||
125,
|
||||
43,
|
||||
|
||||
// G
|
||||
100,
|
||||
139,
|
||||
|
||||
// H
|
||||
125,
|
||||
119,
|
||||
|
||||
// I
|
||||
78,
|
||||
78,
|
||||
|
||||
// J
|
||||
54,
|
||||
77,
|
||||
|
||||
// K
|
||||
139,
|
||||
139,
|
||||
|
||||
// L
|
||||
33,
|
||||
87,
|
||||
|
||||
// M
|
||||
201,
|
||||
117,
|
||||
|
||||
// N
|
||||
162,
|
||||
149,
|
||||
|
||||
// O
|
||||
130,
|
||||
130,
|
||||
|
||||
// P
|
||||
138,
|
||||
60,
|
||||
|
||||
// Q
|
||||
130,
|
||||
172,
|
||||
|
||||
// R
|
||||
149,
|
||||
127,
|
||||
|
||||
// S
|
||||
95,
|
||||
98,
|
||||
|
||||
// T
|
||||
95,
|
||||
25,
|
||||
|
||||
// U
|
||||
118,
|
||||
135,
|
||||
|
||||
// V
|
||||
110,
|
||||
85,
|
||||
|
||||
// W
|
||||
147,
|
||||
175,
|
||||
|
||||
// X
|
||||
105,
|
||||
110,
|
||||
|
||||
// Y
|
||||
121,
|
||||
30,
|
||||
|
||||
// Z
|
||||
101,
|
||||
113,
|
||||
|
||||
// [
|
||||
34,
|
||||
68,
|
||||
|
||||
// \
|
||||
20,
|
||||
26,
|
||||
|
||||
// ]
|
||||
34,
|
||||
68,
|
||||
|
||||
// ^
|
||||
56,
|
||||
0,
|
||||
|
||||
// _
|
||||
0,
|
||||
44,
|
||||
|
||||
// `
|
||||
3,
|
||||
0,
|
||||
|
||||
// a
|
||||
27,
|
||||
175,
|
||||
|
||||
// b
|
||||
80,
|
||||
133,
|
||||
|
||||
// c
|
||||
31,
|
||||
66,
|
||||
|
||||
// d
|
||||
85,
|
||||
147,
|
||||
|
||||
// e
|
||||
32,
|
||||
150,
|
||||
|
||||
// f
|
||||
90,
|
||||
25,
|
||||
|
||||
// g
|
||||
45,
|
||||
230,
|
||||
|
||||
// h
|
||||
77,
|
||||
101,
|
||||
|
||||
// i
|
||||
36,
|
||||
83,
|
||||
|
||||
// j
|
||||
22,
|
||||
84,
|
||||
|
||||
// k
|
||||
71,
|
||||
118,
|
||||
|
||||
// l
|
||||
44,
|
||||
44,
|
||||
|
||||
// m
|
||||
52,
|
||||
172,
|
||||
|
||||
// n
|
||||
38,
|
||||
101,
|
||||
|
||||
// o
|
||||
35,
|
||||
130,
|
||||
|
||||
// p
|
||||
40,
|
||||
197,
|
||||
|
||||
// q
|
||||
43,
|
||||
197,
|
||||
|
||||
// r
|
||||
29,
|
||||
26,
|
||||
|
||||
// s
|
||||
23,
|
||||
103,
|
||||
|
||||
// t
|
||||
67,
|
||||
44,
|
||||
|
||||
// u
|
||||
25,
|
||||
129,
|
||||
|
||||
// v
|
||||
29,
|
||||
85,
|
||||
|
||||
// w
|
||||
27,
|
||||
177,
|
||||
|
||||
// x
|
||||
33,
|
||||
97,
|
||||
|
||||
// y
|
||||
32,
|
||||
145,
|
||||
|
||||
// z
|
||||
33,
|
||||
77,
|
||||
|
||||
// {
|
||||
38,
|
||||
96,
|
||||
|
||||
// |
|
||||
20,
|
||||
55,
|
||||
|
||||
// }
|
||||
36,
|
||||
95,
|
||||
|
||||
// ~
|
||||
2,
|
||||
22,
|
||||
|
||||
];
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Constants } from 'vs/editor/common/core/uint';
|
||||
import { Constants } from 'vs/base/common/uint';
|
||||
import { InlineDecoration, InlineDecorationType } from 'vs/editor/common/viewModel/viewModel';
|
||||
|
||||
export class LineDecoration {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { WrappingIndent } from 'vs/editor/common/config/editorOptions';
|
||||
import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier';
|
||||
import { toUint32Array } from 'vs/editor/common/core/uint';
|
||||
import { toUint32Array } from 'vs/base/common/uint';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ILineMapperFactory, ILineMapping, OutputPosition } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
|
||||
|
||||
63
src/vs/editor/common/viewModel/minimapTokensColorTracker.ts
Normal file
63
src/vs/editor/common/viewModel/minimapTokensColorTracker.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { RGBA8 } from 'vs/editor/common/core/rgba';
|
||||
import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
|
||||
export class MinimapTokensColorTracker {
|
||||
private static _INSTANCE: MinimapTokensColorTracker | null = null;
|
||||
public static getInstance(): MinimapTokensColorTracker {
|
||||
if (!this._INSTANCE) {
|
||||
this._INSTANCE = new MinimapTokensColorTracker();
|
||||
}
|
||||
return this._INSTANCE;
|
||||
}
|
||||
|
||||
private _colors!: RGBA8[];
|
||||
private _backgroundIsLight!: boolean;
|
||||
|
||||
private readonly _onDidChange = new Emitter<void>();
|
||||
public readonly onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private constructor() {
|
||||
this._updateColorMap();
|
||||
TokenizationRegistry.onDidChange(e => {
|
||||
if (e.changedColorMap) {
|
||||
this._updateColorMap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _updateColorMap(): void {
|
||||
const colorMap = TokenizationRegistry.getColorMap();
|
||||
if (!colorMap) {
|
||||
this._colors = [RGBA8.Empty];
|
||||
this._backgroundIsLight = true;
|
||||
return;
|
||||
}
|
||||
this._colors = [RGBA8.Empty];
|
||||
for (let colorId = 1; colorId < colorMap.length; colorId++) {
|
||||
const source = colorMap[colorId].rgba;
|
||||
// Use a VM friendly data-type
|
||||
this._colors[colorId] = new RGBA8(source.r, source.g, source.b, Math.round(source.a * 255));
|
||||
}
|
||||
let backgroundLuminosity = colorMap[ColorId.DefaultBackground].getRelativeLuminance();
|
||||
this._backgroundIsLight = backgroundLuminosity >= 0.5;
|
||||
this._onDidChange.fire(undefined);
|
||||
}
|
||||
|
||||
public getColor(colorId: ColorId): RGBA8 {
|
||||
if (colorId < 1 || colorId >= this._colors.length) {
|
||||
// background color (basically invisible)
|
||||
colorId = ColorId.DefaultBackground;
|
||||
}
|
||||
return this._colors[colorId];
|
||||
}
|
||||
|
||||
public backgroundIsLight(): boolean {
|
||||
return this._backgroundIsLight;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { toUint32 } from 'vs/editor/common/core/uint';
|
||||
import { toUint32 } from 'vs/base/common/uint';
|
||||
|
||||
export class PrefixSumIndexOfResult {
|
||||
_prefixSumIndexOfResultBrand: void;
|
||||
|
||||
@@ -15,7 +15,7 @@ import { ModelDecorationOverviewRulerOptions, ModelDecorationMinimapOptions } fr
|
||||
import * as textModelEvents from 'vs/editor/common/model/textModelEvents';
|
||||
import { ColorId, LanguageId, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
|
||||
import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
import { MinimapTokensColorTracker } from 'vs/editor/common/viewModel/minimapTokensColorTracker';
|
||||
import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { ViewLayout } from 'vs/editor/common/viewLayout/viewLayout';
|
||||
import { CharacterHardWrappingLineMapperFactory } from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
|
||||
@@ -24,6 +24,7 @@ import { ICoordinatesConverter, IOverviewRulerDecorations, IViewModel, MinimapLi
|
||||
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
|
||||
const USE_IDENTITY_LINES_COLLECTION = true;
|
||||
|
||||
@@ -128,6 +129,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
super.dispose();
|
||||
this.decorations.dispose();
|
||||
this.lines.dispose();
|
||||
this.invalidateMinimapColorCache();
|
||||
this.viewportStartLineTrackedRange = this.model._setTrackedRange(this.viewportStartLineTrackedRange, null, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
}
|
||||
|
||||
@@ -626,9 +628,19 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
|
||||
ranges = ranges.slice(0);
|
||||
ranges.sort(Range.compareRangesUsingStarts);
|
||||
const nonEmptyRanges = ranges.filter((r) => !r.isEmpty());
|
||||
|
||||
if (nonEmptyRanges.length === 0) {
|
||||
let hasEmptyRange = false;
|
||||
let hasNonEmptyRange = false;
|
||||
for (const range of ranges) {
|
||||
if (range.isEmpty()) {
|
||||
hasEmptyRange = true;
|
||||
} else {
|
||||
hasNonEmptyRange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasNonEmptyRange) {
|
||||
// all ranges are empty
|
||||
if (!emptySelectionClipboard) {
|
||||
return '';
|
||||
}
|
||||
@@ -648,9 +660,29 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
return result;
|
||||
}
|
||||
|
||||
if (hasEmptyRange && emptySelectionClipboard) {
|
||||
// mixed empty selections and non-empty selections
|
||||
let result: string[] = [];
|
||||
let prevModelLineNumber = 0;
|
||||
for (const range of ranges) {
|
||||
const modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
|
||||
if (range.isEmpty()) {
|
||||
if (modelLineNumber !== prevModelLineNumber) {
|
||||
result.push(this.model.getLineContent(modelLineNumber));
|
||||
}
|
||||
} else {
|
||||
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
|
||||
}
|
||||
prevModelLineNumber = modelLineNumber;
|
||||
}
|
||||
return result.length === 1 ? result[0] : result;
|
||||
}
|
||||
|
||||
let result: string[] = [];
|
||||
for (const nonEmptyRange of nonEmptyRanges) {
|
||||
result.push(this.getValueInRange(nonEmptyRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
|
||||
for (const range of ranges) {
|
||||
if (!range.isEmpty()) {
|
||||
result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined));
|
||||
}
|
||||
}
|
||||
return result.length === 1 ? result[0] : result;
|
||||
}
|
||||
@@ -713,7 +745,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel
|
||||
if (lineContent === '') {
|
||||
result += '<br>';
|
||||
} else {
|
||||
result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize);
|
||||
result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user