Merge branch 'master' into yarn

This commit is contained in:
Joao Moreno 2017-11-14 18:46:07 +01:00
commit 5e5ebf5c1f
31 changed files with 452 additions and 294 deletions

View file

@ -210,8 +210,7 @@ function uglifyWithCopyrights() {
return stream.pipe(minify({
output: {
comments: preserveComments(f),
// linux tfs build agent is crashing, does this help?§
max_line_len: 3200000
max_line_len: 1024
}
}));
}));

View file

@ -285,8 +285,7 @@ function uglifyWithCopyrights(): NodeJS.ReadWriteStream {
return stream.pipe(minify({
output: {
comments: preserveComments(<FileWithCopyright>f),
// linux tfs build agent is crashing, does this help?§
max_line_len: 3200000
max_line_len: 1024
}
}));
}));

View file

@ -11,47 +11,5 @@
},
"bugs": {
"url": "https://github.com/Microsoft/vscode/issues"
},
"devDependencies": {
"@types/minimist": "1.2.0",
"@types/mocha": "2.2.39",
"@types/semver": "5.3.30",
"@types/sinon": "1.16.34",
"debounce": "^1.0.0",
"eslint": "^3.4.0",
"event-stream": "^3.1.7",
"ghooks": "1.0.3",
"glob": "^5.0.13",
"gulp": "^3.8.9",
"gulp-bom": "^1.0.0",
"gulp-concat": "^2.6.0",
"gulp-cssnano": "^2.1.0",
"gulp-filter": "^3.0.0",
"gulp-flatmap": "^1.0.0",
"gulp-rename": "^1.2.0",
"gulp-sourcemaps": "^1.11.0",
"gulp-tsb": "^2.0.3",
"gulp-tslint": "^7.0.1",
"gulp-uglify": "^2.0.0",
"gulp-util": "^3.0.6",
"gulp-watch": "^4.3.9",
"is": "^3.1.0",
"istanbul": "^0.3.17",
"jsdom-no-contextify": "^3.1.0",
"lazy.js": "^0.4.2",
"minimatch": "^2.0.10",
"mocha": "^2.2.5",
"object-assign": "^4.0.1",
"pump": "^1.0.1",
"remap-istanbul": "^0.6.4",
"rimraf": "^2.2.8",
"sinon": "^1.17.2",
"source-map": "^0.4.4",
"tslint": "^4.3.1",
"typescript": "2.6.1",
"typescript-formatter": "4.0.1",
"underscore": "^1.8.2",
"vinyl": "^0.4.5",
"vscode-nls-dev": "^2.0.1"
}
}

View file

@ -3266,7 +3266,6 @@
<string>(?x:
(\!\[)((?&lt;square&gt;[^\[\]\\]|\\.|\[\g&lt;square&gt;*+\])*+)(\])
# Match the link text.
([ ])? # Space not allowed
(\() # Opening paren for url
(&lt;?)(\S+?)(&gt;?) # The url
[ \t]* # Optional whitespace
@ -3578,7 +3577,6 @@
<string>(?x:
(\[)((?&lt;square&gt;[^\[\]\\]|\\.|\[\g&lt;square&gt;*+\])*+)(\])
# Match the link text.
([ ])? # Space not allowed
(\() # Opening paren for url
(&lt;?)(.*?)(&gt;?) # The url
[ \t]* # Optional whitespace

View file

@ -756,7 +756,6 @@
<string>(?x:
(\!\[)((?&lt;square&gt;[^\[\]\\]|\\.|\[\g&lt;square&gt;*+\])*+)(\])
# Match the link text.
([ ])? # Space not allowed
(\() # Opening paren for url
(&lt;?)(\S+?)(&gt;?) # The url
[ \t]* # Optional whitespace
@ -1068,7 +1067,6 @@
<string>(?x:
(\[)((?&lt;square&gt;[^\[\]\\]|\\.|\[\g&lt;square&gt;*+\])*+)(\])
# Match the link text.
([ ])? # Space not allowed
(\() # Opening paren for url
(&lt;?)(.*?)(&gt;?) # The url
[ \t]* # Optional whitespace

View file

@ -9,7 +9,7 @@
"contributes": {
"languages": [{
"id": "shellscript",
"aliases": ["Shell Script (Bash)", "shellscript", "bash", "sh", "zsh"],
"aliases": ["Shell Script", "shellscript", "bash", "sh", "zsh"],
"extensions": [".sh", ".bash", ".bashrc", ".bash_aliases", ".bash_profile", ".bash_login", ".ebuild", ".install", ".profile", ".bash_logout", ".zsh", ".zshrc", ".zprofile", ".zlogin", ".zlogout", ".zshenv", ".zsh-theme"],
"filenames": ["PKGBUILD"],
"firstLine": "^#!.*\\b(bash|zsh|sh|tcsh).*|^#\\s*-\\*-[^*]*mode:\\s*shell-script[^*]*-\\*-",

View file

@ -14,7 +14,6 @@
"postinstall": "node build/npm/postinstall.js",
"compile": "gulp compile --max_old_space_size=4096",
"watch": "gulp watch --max_old_space_size=4096",
"monaco-editor-setup": "node scripts/monaco-editor-setup.js",
"monaco-editor-test": "mocha --only-monaco-editor",
"precommit": "node build/gulpfile.hygiene.js",
"gulp": "gulp --max_old_space_size=4096",

View file

@ -1,38 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
var fs = require('fs');
var cp = require('child_process');
var path = require('path');
var ROOT = path.join(__dirname, '..');
var ROOT_NODE_MODULES_PATH = path.join(ROOT, 'node_modules');
var EDITOR_ROOT = path.join(ROOT, 'build/monaco')
var EDITOR_NODE_MODULES_PATH = path.join(EDITOR_ROOT, 'node_modules')
var cmd = `npm install`;
cp.execSync(cmd, {
cwd: EDITOR_ROOT,
stdio:[0,1,2]
});
if (!fs.existsSync(ROOT_NODE_MODULES_PATH)) {
fs.mkdirSync(ROOT_NODE_MODULES_PATH);
}
// Move deps over
var modules = fs.readdirSync(EDITOR_NODE_MODULES_PATH);
modules.forEach(function(module) {
var src = path.join(EDITOR_NODE_MODULES_PATH, module);
var dst = path.join(ROOT_NODE_MODULES_PATH, module);
if (!fs.existsSync(dst)) {
console.log('Moving ' + module + '...');
fs.renameSync(src, dst);
} else {
console.log('Skipping moving ' + module + '.');
}
});

View file

@ -13,9 +13,9 @@ fi
cd $ROOT
# Integration Tests
#./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/code.sh $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
#./scripts/test-int-mocha.sh
#./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
./scripts/test-int-mocha.sh
./scripts/code.sh $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disableExtensions --user-data-dir=$VSCODEUSERDATADIR --skip-getting-started
rm -r $VSCODEUSERDATADIR

View file

@ -25,6 +25,7 @@ import { PartFingerprints, PartFingerprint, ViewPart } from 'vs/editor/browser/v
import { Margin } from 'vs/editor/browser/viewParts/margin/margin';
import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers';
import { BareFontInfo } from 'vs/editor/common/config/fontInfo';
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
export interface ITextAreaHandlerHelper {
visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalRange;
@ -481,7 +482,7 @@ export class TextAreaHandler extends ViewPart {
if (this._context.configuration.editor.viewInfo.glyphMargin) {
tac.setClassName('monaco-editor-background textAreaCover ' + Margin.CLASS_NAME);
} else {
if (this._context.configuration.editor.viewInfo.renderLineNumbers) {
if (this._context.configuration.editor.viewInfo.renderLineNumbers !== RenderLineNumbersType.Off) {
tac.setClassName('monaco-editor-background textAreaCover ' + LineNumbersOverlay.CLASS_NAME);
} else {
tac.setClassName('monaco-editor-background textAreaCover');

View file

@ -14,6 +14,7 @@ import { ViewContext } from 'vs/editor/common/view/viewContext';
import { RenderingContext } from 'vs/editor/common/view/renderingContext';
import * as viewEvents from 'vs/editor/common/view/viewEvents';
import { Position } from 'vs/editor/common/core/position';
import { RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
export class LineNumbersOverlay extends DynamicViewOverlay {
@ -22,12 +23,10 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
private _context: ViewContext;
private _lineHeight: number;
private _renderLineNumbers: boolean;
private _renderLineNumbers: RenderLineNumbersType;
private _renderCustomLineNumbers: (lineNumber: number) => string;
private _renderRelativeLineNumbers: boolean;
private _lineNumbersLeft: number;
private _lineNumbersWidth: number;
private _lastCursorModelPosition: Position;
private _renderResult: string[];
@ -47,7 +46,6 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
this._lineHeight = config.lineHeight;
this._renderLineNumbers = config.viewInfo.renderLineNumbers;
this._renderCustomLineNumbers = config.viewInfo.renderCustomLineNumbers;
this._renderRelativeLineNumbers = config.viewInfo.renderRelativeLineNumbers;
this._lineNumbersLeft = config.layoutInfo.lineNumbersLeft;
this._lineNumbersWidth = config.layoutInfo.lineNumbersWidth;
}
@ -69,7 +67,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
const primaryViewPosition = e.selections[0].getPosition();
this._lastCursorModelPosition = this._context.model.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition);
if (this._renderRelativeLineNumbers) {
if (this._renderLineNumbers === RenderLineNumbersType.Relative || this._renderLineNumbers === RenderLineNumbersType.Interval) {
return true;
}
return false;
@ -106,7 +104,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
return this._renderCustomLineNumbers(modelLineNumber);
}
if (this._renderRelativeLineNumbers) {
if (this._renderLineNumbers === RenderLineNumbersType.Relative) {
let diff = Math.abs(this._lastCursorModelPosition.lineNumber - modelLineNumber);
if (diff === 0) {
return '<span class="relative-current-line-number">' + modelLineNumber + '</span>';
@ -114,11 +112,21 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
return String(diff);
}
if (this._renderLineNumbers === RenderLineNumbersType.Interval) {
if (this._lastCursorModelPosition.lineNumber === modelLineNumber) {
return String(modelLineNumber);
}
if (modelLineNumber % 10 === 0) {
return String(modelLineNumber);
}
return '';
}
return String(modelLineNumber);
}
public prepareRender(ctx: RenderingContext): void {
if (!this._renderLineNumbers) {
if (this._renderLineNumbers === RenderLineNumbersType.Off) {
this._renderResult = null;
return;
}
@ -133,6 +141,7 @@ export class LineNumbersOverlay extends DynamicViewOverlay {
let lineIndex = lineNumber - visibleStartLineNumber;
let renderLineNumber = this._getLineRenderLineNumber(lineNumber);
if (renderLineNumber) {
output[lineIndex] = (
common
@ -166,4 +175,4 @@ registerThemingParticipant((theme, collector) => {
if (lineNumbers) {
collector.addRule(`.monaco-editor .line-numbers { color: ${lineNumbers}; }`);
}
});
});

View file

@ -203,9 +203,15 @@ const editorConfiguration: IConfigurationNode = {
},
'editor.lineNumbers': {
'type': 'string',
'enum': ['off', 'on', 'relative'],
'enum': ['off', 'on', 'relative', 'interval'],
'enumDescriptions': [
nls.localize('lineNumbers.off', "Line numbers are not rendered."),
nls.localize('lineNumbers.on', "Line numbers are rendered as absolute number."),
nls.localize('lineNumbers.relative', "Line numbers are rendered as distance in lines to cursor position."),
nls.localize('lineNumbers.interval', "Line numbers are rendered every 10 lines.")
],
'default': 'on',
'description': nls.localize('lineNumbers', "Controls the display of line numbers. Possible values are 'on', 'off', and 'relative'. 'relative' shows the line count from the current cursor position.")
'description': nls.localize('lineNumbers', "Controls the display of line numbers. Possible values are 'on', 'off', and 'relative'.")
},
'editor.rulers': {
'type': 'array',

View file

@ -160,7 +160,7 @@ export interface IEditorOptions {
* Otherwise, line numbers will not be rendered.
* Defaults to true.
*/
lineNumbers?: 'on' | 'off' | 'relative' | ((lineNumber: number) => string);
lineNumbers?: 'on' | 'off' | 'relative' | 'interval' | ((lineNumber: number) => string);
/**
* Should the corresponding line be selected when clicking on the line number?
* Defaults to true.
@ -753,14 +753,21 @@ export interface EditorWrappingInfo {
readonly wordWrapBreakObtrusiveCharacters: string;
}
export const enum RenderLineNumbersType {
Off = 0,
On = 1,
Relative = 2,
Interval = 3,
Custom = 4
}
export interface InternalEditorViewOptions {
readonly extraEditorClassName: string;
readonly disableMonospaceOptimizations: boolean;
readonly rulers: number[];
readonly ariaLabel: string;
readonly renderLineNumbers: boolean;
readonly renderLineNumbers: RenderLineNumbersType;
readonly renderCustomLineNumbers: (lineNumber: number) => string;
readonly renderRelativeLineNumbers: boolean;
readonly selectOnLineNumbers: boolean;
readonly glyphMargin: boolean;
readonly revealHorizontalRightPadding: number;
@ -1029,7 +1036,6 @@ export class InternalEditorOptions {
&& a.ariaLabel === b.ariaLabel
&& a.renderLineNumbers === b.renderLineNumbers
&& a.renderCustomLineNumbers === b.renderCustomLineNumbers
&& a.renderRelativeLineNumbers === b.renderRelativeLineNumbers
&& a.selectOnLineNumbers === b.selectOnLineNumbers
&& a.glyphMargin === b.glyphMargin
&& a.revealHorizontalRightPadding === b.revealHorizontalRightPadding
@ -1558,9 +1564,8 @@ export class EditorOptionsValidator {
rulers.sort();
}
let renderLineNumbers: boolean = defaults.renderLineNumbers;
let renderLineNumbers: RenderLineNumbersType = defaults.renderLineNumbers;
let renderCustomLineNumbers: (lineNumber: number) => string = defaults.renderCustomLineNumbers;
let renderRelativeLineNumbers: boolean = defaults.renderRelativeLineNumbers;
if (typeof opts.lineNumbers !== 'undefined') {
let lineNumbers = opts.lineNumbers;
@ -1573,21 +1578,16 @@ export class EditorOptionsValidator {
}
if (typeof lineNumbers === 'function') {
renderLineNumbers = true;
renderLineNumbers = RenderLineNumbersType.Custom;
renderCustomLineNumbers = lineNumbers;
renderRelativeLineNumbers = false;
} else if (lineNumbers === 'interval') {
renderLineNumbers = RenderLineNumbersType.Interval;
} else if (lineNumbers === 'relative') {
renderLineNumbers = true;
renderCustomLineNumbers = null;
renderRelativeLineNumbers = true;
renderLineNumbers = RenderLineNumbersType.Relative;
} else if (lineNumbers === 'on') {
renderLineNumbers = true;
renderCustomLineNumbers = null;
renderRelativeLineNumbers = false;
renderLineNumbers = RenderLineNumbersType.On;
} else {
renderLineNumbers = false;
renderCustomLineNumbers = null;
renderRelativeLineNumbers = false;
renderLineNumbers = RenderLineNumbersType.Off;
}
}
@ -1627,7 +1627,6 @@ export class EditorOptionsValidator {
ariaLabel: _string(opts.ariaLabel, defaults.ariaLabel),
renderLineNumbers: renderLineNumbers,
renderCustomLineNumbers: renderCustomLineNumbers,
renderRelativeLineNumbers: renderRelativeLineNumbers,
selectOnLineNumbers: _boolean(opts.selectOnLineNumbers, defaults.selectOnLineNumbers),
glyphMargin: _boolean(opts.glyphMargin, defaults.glyphMargin),
revealHorizontalRightPadding: _clampedInt(opts.revealHorizontalRightPadding, defaults.revealHorizontalRightPadding, 0, 1000),
@ -1730,7 +1729,6 @@ export class InternalEditorOptionsFactory {
ariaLabel: (accessibilityIsOff ? nls.localize('accessibilityOffAriaLabel', "The editor is not accessible at this time. Press Alt+F1 for options.") : opts.viewInfo.ariaLabel),
renderLineNumbers: opts.viewInfo.renderLineNumbers,
renderCustomLineNumbers: opts.viewInfo.renderCustomLineNumbers,
renderRelativeLineNumbers: opts.viewInfo.renderRelativeLineNumbers,
selectOnLineNumbers: opts.viewInfo.selectOnLineNumbers,
glyphMargin: opts.viewInfo.glyphMargin,
revealHorizontalRightPadding: opts.viewInfo.revealHorizontalRightPadding,
@ -1822,7 +1820,7 @@ export class InternalEditorOptionsFactory {
outerHeight: env.outerHeight,
showGlyphMargin: opts.viewInfo.glyphMargin,
lineHeight: env.fontInfo.lineHeight,
showLineNumbers: opts.viewInfo.renderLineNumbers,
showLineNumbers: (opts.viewInfo.renderLineNumbers !== RenderLineNumbersType.Off),
lineNumbersMinChars: opts.lineNumbersMinChars,
lineNumbersDigitCount: env.lineNumbersDigitCount,
lineDecorationsWidth: lineDecorationsWidth,
@ -2152,9 +2150,8 @@ export const EDITOR_DEFAULTS: IValidatedEditorOptions = {
disableMonospaceOptimizations: false,
rulers: [],
ariaLabel: nls.localize('editorViewAccessibleLabel', "Editor content"),
renderLineNumbers: true,
renderLineNumbers: RenderLineNumbersType.On,
renderCustomLineNumbers: null,
renderRelativeLineNumbers: false,
selectOnLineNumbers: true,
glyphMargin: true,
revealHorizontalRightPadding: 30,

View file

@ -401,39 +401,46 @@ suite('Folding Model', () => {
/* 10*/ ' return;',
/* 11*/ ' }',
/* 12*/ ' }',
/* 13*/ '}'];
/* 13*/ ' //#region',
/* 14*/ ' const bar = 9;',
/* 15*/ ' //#endregion',
/* 16*/ '}'];
let textModel = Model.createFromString(lines.join('\n'));
try {
let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel));
let ranges = computeRanges(textModel, false, { start: /^\/\/#region$/, end: /^\/\/#endregion$/ });
let ranges = computeRanges(textModel, false, { start: /^\s*\/\/#region$/, end: /^\s*\/\/#endregion$/ });
foldingModel.update(ranges);
let r1 = r(1, 2, false);
let r2 = r(3, 12, false);
let r2 = r(3, 15, false);
let r3 = r(4, 11, false);
let r4 = r(5, 6, false);
let r5 = r(9, 10, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5]);
let r6 = r(13, 15, false);
assertRanges(foldingModel, [r1, r2, r3, r4, r5, r6]);
setCollapseStateAtLevel(foldingModel, 1, true, []);
assertFoldedRanges(foldingModel, [r1, r2], '1');
setCollapseStateAtLevel(foldingModel, 1, false, [5]);
assertFoldedRanges(foldingModel, [r2], '1');
assertFoldedRanges(foldingModel, [r2], '2');
setCollapseStateAtLevel(foldingModel, 1, false, [1]);
assertFoldedRanges(foldingModel, [], '1');
assertFoldedRanges(foldingModel, [], '3');
setCollapseStateAtLevel(foldingModel, 2, true, []);
assertFoldedRanges(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3, r6], '4');
setCollapseStateAtLevel(foldingModel, 2, false, [5, 6]);
assertFoldedRanges(foldingModel, [r3], '5');
setCollapseStateAtLevel(foldingModel, 3, true, [4, 9]);
assertFoldedRanges(foldingModel, [r3, r4], '1');
assertFoldedRanges(foldingModel, [r3, r4], '6');
setCollapseStateAtLevel(foldingModel, 3, false, [4, 9]);
assertFoldedRanges(foldingModel, [r3], '1');
assertFoldedRanges(foldingModel, [r3], '7');
} finally {
textModel.dispose();
}

View file

@ -140,7 +140,7 @@ class MoveLinesDownAction extends AbstractMoveLinesAction {
}
}
abstract class AbstractSortLinesAction extends EditorAction {
export abstract class AbstractSortLinesAction extends EditorAction {
private descending: boolean;
constructor(descending: boolean, opts: IActionOptions) {
@ -149,16 +149,18 @@ abstract class AbstractSortLinesAction extends EditorAction {
}
public run(accessor: ServicesAccessor, editor: ICodeEditor): void {
const selections = editor.getSelections();
if (!SortLinesCommand.canRun(editor.getModel(), editor.getSelection(), this.descending)) {
return;
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
if (!SortLinesCommand.canRun(editor.getModel(), selection, this.descending)) {
return;
}
}
var commands: ICommand[] = [];
var selections = editor.getSelections();
for (var i = 0; i < selections.length; i++) {
commands.push(new SortLinesCommand(selections[i], this.descending));
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {
commands[i] = new SortLinesCommand(selections[i], this.descending);
}
editor.pushUndoStop();
@ -167,7 +169,7 @@ abstract class AbstractSortLinesAction extends EditorAction {
}
}
class SortLinesAscendingAction extends AbstractSortLinesAction {
export class SortLinesAscendingAction extends AbstractSortLinesAction {
constructor() {
super(false, {
id: 'editor.action.sortLinesAscending',
@ -178,7 +180,7 @@ class SortLinesAscendingAction extends AbstractSortLinesAction {
}
}
class SortLinesDescendingAction extends AbstractSortLinesAction {
export class SortLinesDescendingAction extends AbstractSortLinesAction {
constructor() {
super(true, {
id: 'editor.action.sortLinesDescending',

View file

@ -9,12 +9,129 @@ import { Selection } from 'vs/editor/common/core/selection';
import { Position } from 'vs/editor/common/core/position';
import { Handler, IModel, DefaultEndOfLine } from 'vs/editor/common/editorCommon';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { DeleteAllLeftAction, JoinLinesAction, TransposeAction, UpperCaseAction, LowerCaseAction, DeleteAllRightAction, InsertLineBeforeAction, InsertLineAfterAction, IndentLinesAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { DeleteAllLeftAction, JoinLinesAction, TransposeAction, UpperCaseAction, LowerCaseAction, DeleteAllRightAction, InsertLineBeforeAction, InsertLineAfterAction, IndentLinesAction, SortLinesAscendingAction, SortLinesDescendingAction } from 'vs/editor/contrib/linesOperations/linesOperations';
import { Cursor } from 'vs/editor/common/controller/cursor';
import { Model } from 'vs/editor/common/model/model';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
suite('Editor Contrib - Line Operations', () => {
suite('SortLinesAscendingAction', () => {
test('should sort selected lines in ascending order', function () {
withTestCodeEditor(
[
'omicron',
'beta',
'alpha'
], {}, (editor, cursor) => {
let model = editor.getModel();
let sortLinesAscendingAction = new SortLinesAscendingAction();
editor.setSelection(new Selection(1, 1, 3, 5));
sortLinesAscendingAction.run(null, editor);
assert.deepEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron'
]);
assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 3, 7).toString());
});
});
test('should sort multiple selections in ascending order', function () {
withTestCodeEditor(
[
'omicron',
'beta',
'alpha',
'',
'omicron',
'beta',
'alpha'
], {}, (editor, cursor) => {
let model = editor.getModel();
let sortLinesAscendingAction = new SortLinesAscendingAction();
editor.setSelections([new Selection(1, 1, 3, 5), new Selection(5, 1, 7, 5)]);
sortLinesAscendingAction.run(null, editor);
assert.deepEqual(model.getLinesContent(), [
'alpha',
'beta',
'omicron',
'',
'alpha',
'beta',
'omicron'
]);
let expectedSelections = [
new Selection(1, 1, 3, 7),
new Selection(5, 1, 7, 7)
];
editor.getSelections().forEach((actualSelection, index) => {
assert.deepEqual(actualSelection.toString(), expectedSelections[index].toString());
});
});
});
});
suite('SortLinesDescendingAction', () => {
test('should sort selected lines in descending order', function () {
withTestCodeEditor(
[
'alpha',
'beta',
'omicron'
], {}, (editor, cursor) => {
let model = editor.getModel();
let sortLinesDescendingAction = new SortLinesDescendingAction();
editor.setSelection(new Selection(1, 1, 3, 7));
sortLinesDescendingAction.run(null, editor);
assert.deepEqual(model.getLinesContent(), [
'omicron',
'beta',
'alpha'
]);
assert.deepEqual(editor.getSelection().toString(), new Selection(1, 1, 3, 5).toString());
});
});
test('should sort multiple selections in descending order', function () {
withTestCodeEditor(
[
'alpha',
'beta',
'omicron',
'',
'alpha',
'beta',
'omicron'
], {}, (editor, cursor) => {
let model = editor.getModel();
let sortLinesDescendingAction = new SortLinesDescendingAction();
editor.setSelections([new Selection(1, 1, 3, 7), new Selection(5, 1, 7, 7)]);
sortLinesDescendingAction.run(null, editor);
assert.deepEqual(model.getLinesContent(), [
'omicron',
'beta',
'alpha',
'',
'omicron',
'beta',
'alpha'
]);
let expectedSelections = [
new Selection(1, 1, 3, 5),
new Selection(5, 1, 7, 5)
];
editor.getSelections().forEach((actualSelection, index) => {
assert.deepEqual(actualSelection.toString(), expectedSelections[index].toString());
});
});
});
});
suite('DeleteAllLeftAction', () => {
test('should delete to the left of the cursor', function () {
withTestCodeEditor(

View file

@ -123,7 +123,7 @@ suite('Editor Contrib - Sort Lines Command', () => {
);
});
test('sorting first 4 lines desscending', function () {
test('sorting first 4 lines descending', function () {
testSortLinesDescendingCommand(
[
'first',

View file

@ -338,6 +338,21 @@ enum ScrollType {
Immediate = 1,
}
/**
* @internal
* --------------------------------------------
* This is repeated here so it can be exported
* because TS inlines const enums
* --------------------------------------------
*/
enum RenderLineNumbersType {
Off = 0,
On = 1,
Relative = 2,
Interval = 3,
Custom = 4
}
/**
* @internal
*/
@ -384,6 +399,7 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
OverlayWidgetPositionPreference: OverlayWidgetPositionPreference,
RenderMinimap: editorOptions.RenderMinimap,
ScrollType: <any>ScrollType,
RenderLineNumbersType: <any>RenderLineNumbersType,
// classes
InternalEditorOptions: <any>editorOptions.InternalEditorOptions,

13
src/vs/monaco.d.ts vendored
View file

@ -2498,7 +2498,7 @@ declare module monaco.editor {
* Otherwise, line numbers will not be rendered.
* Defaults to true.
*/
lineNumbers?: 'on' | 'off' | 'relative' | ((lineNumber: number) => string);
lineNumbers?: 'on' | 'off' | 'relative' | 'interval' | ((lineNumber: number) => string);
/**
* Should the corresponding line be selected when clicking on the line number?
* Defaults to true.
@ -3024,14 +3024,21 @@ declare module monaco.editor {
readonly wordWrapBreakObtrusiveCharacters: string;
}
export const enum RenderLineNumbersType {
Off = 0,
On = 1,
Relative = 2,
Interval = 3,
Custom = 4,
}
export interface InternalEditorViewOptions {
readonly extraEditorClassName: string;
readonly disableMonospaceOptimizations: boolean;
readonly rulers: number[];
readonly ariaLabel: string;
readonly renderLineNumbers: boolean;
readonly renderLineNumbers: RenderLineNumbersType;
readonly renderCustomLineNumbers: (lineNumber: number) => string;
readonly renderRelativeLineNumbers: boolean;
readonly selectOnLineNumbers: boolean;
readonly glyphMargin: boolean;
readonly revealHorizontalRightPadding: number;

View file

@ -20,6 +20,7 @@ import { IWorkspaceIdentifier, IWorkspacesMainService, getWorkspaceLabel, ISingl
import { IHistoryMainService, IRecentlyOpened } from 'vs/platform/history/common/history';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { isEqual } from 'vs/base/common/paths';
import { RunOnceScheduler } from 'vs/base/common/async';
export interface ILegacyRecentlyOpened extends IRecentlyOpened {
folders: string[]; // TODO@Ben migration
@ -28,6 +29,7 @@ export interface ILegacyRecentlyOpened extends IRecentlyOpened {
export class HistoryMainService implements IHistoryMainService {
private static MAX_TOTAL_RECENT_ENTRIES = 100;
private static MAX_MACOS_DOCK_RECENT_ENTRIES = 10;
private static recentlyOpenedStorageKey = 'openedPathsList';
@ -36,12 +38,16 @@ export class HistoryMainService implements IHistoryMainService {
private _onRecentlyOpenedChange = new Emitter<void>();
onRecentlyOpenedChange: CommonEvent<void> = this._onRecentlyOpenedChange.event;
private macOSRecentDocumentsUpdater: RunOnceScheduler;
constructor(
@IStorageService private storageService: IStorageService,
@ILogService private logService: ILogService,
@IWorkspacesMainService private workspacesService: IWorkspacesMainService,
@IEnvironmentService private environmentService: IEnvironmentService,
) {
this.macOSRecentDocumentsUpdater = new RunOnceScheduler(() => this.updateMacOSRecentDocuments(), 800);
this.registerListeners();
}
@ -69,10 +75,8 @@ export class HistoryMainService implements IHistoryMainService {
mru.workspaces.unshift(workspace);
mru.workspaces = arrays.distinct(mru.workspaces, workspace => this.distinctFn(workspace));
// Add to recent documents (macOS only, Windows can show workspaces separately)
if (isMacintosh) {
app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath);
}
// We do not add to recent documents here because on Windows we do this from a custom
// JumpList and on macOS we fill the recent documents in one go from all our data later.
});
// Files
@ -80,8 +84,8 @@ export class HistoryMainService implements IHistoryMainService {
mru.files.unshift(path);
mru.files = arrays.distinct(mru.files, file => this.distinctFn(file));
// Add to recent documents (Windows/macOS only)
if (isMacintosh || isWindows) {
// Add to recent documents (Windows only, macOS later)
if (isWindows) {
app.addRecentDocument(path);
}
});
@ -92,6 +96,11 @@ export class HistoryMainService implements IHistoryMainService {
this.saveRecentlyOpened(mru);
this._onRecentlyOpenedChange.fire();
// Schedule update to recent documents on macOS dock
if (isMacintosh) {
this.macOSRecentDocumentsUpdater.schedule();
}
}
}
@ -119,6 +128,41 @@ export class HistoryMainService implements IHistoryMainService {
if (update) {
this.saveRecentlyOpened(mru);
this._onRecentlyOpenedChange.fire();
// Schedule update to recent documents on macOS dock
if (isMacintosh) {
this.macOSRecentDocumentsUpdater.schedule();
}
}
}
private updateMacOSRecentDocuments(): void {
if (!isMacintosh) {
return;
}
// macOS recent documents in the dock are behaving strangely. the entries seem to get
// out of sync quickly over time. the attempted fix is to always set the list fresh
// from our MRU history data. So we clear the documents first and then set the documents
// again.
app.clearRecentDocuments();
const mru = this.getRecentlyOpened();
let maxEntries = HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES;
// Take up to maxEntries/2 workspaces
for (let i = 0; i < mru.workspaces.length && i < HistoryMainService.MAX_MACOS_DOCK_RECENT_ENTRIES / 2; i++) {
const workspace = mru.workspaces[i];
app.addRecentDocument(isSingleFolderWorkspaceIdentifier(workspace) ? workspace : workspace.configPath);
maxEntries--;
}
// Take up to maxEntries files
for (let i = 0; i < mru.files.length && i < maxEntries; i++) {
const file = mru.files[i];
app.addRecentDocument(file);
}
}

View file

@ -13,7 +13,7 @@ import { Range, IRange } from 'vs/editor/common/core/range';
import { Selection, ISelection } from 'vs/editor/common/core/selection';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { EndOfLine, TextEditorLineNumbersStyle } from 'vs/workbench/api/node/extHostTypes';
import { TextEditorCursorStyle, cursorStyleToString } from 'vs/editor/common/config/editorOptions';
import { TextEditorCursorStyle, cursorStyleToString, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
import { ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IResolvedTextEditorConfiguration, ISelectionChangeEvent, ITextEditorConfigurationUpdate, TextEditorRevealType, IApplyEditsOptions, IUndoStopOptions } from 'vs/workbench/api/node/extHost.protocol';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@ -290,12 +290,16 @@ export class MainThreadTextEditor {
let codeEditorOpts = codeEditor.getConfiguration();
cursorStyle = codeEditorOpts.viewInfo.cursorStyle;
if (codeEditorOpts.viewInfo.renderRelativeLineNumbers) {
lineNumbers = TextEditorLineNumbersStyle.Relative;
} else if (codeEditorOpts.viewInfo.renderLineNumbers) {
lineNumbers = TextEditorLineNumbersStyle.On;
} else {
lineNumbers = TextEditorLineNumbersStyle.Off;
switch (codeEditorOpts.viewInfo.renderLineNumbers) {
case RenderLineNumbersType.Off:
lineNumbers = TextEditorLineNumbersStyle.Off;
break;
case RenderLineNumbersType.Relative:
lineNumbers = TextEditorLineNumbersStyle.Relative;
break;
default:
lineNumbers = TextEditorLineNumbersStyle.On;
break;
}
}

View file

@ -45,6 +45,7 @@ import { RunOnceScheduler } from 'vs/base/common/async';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ConfigurationTarget, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
const TextInputActions: IAction[] = [
new Action('undo', nls.localize('undo', "Undo"), null, true, () => document.execCommand('undo') && TPromise.as(true)),
@ -68,6 +69,9 @@ export class ElectronWindow extends Themable {
private previousConfiguredZoomLevel: number;
private addFoldersScheduler: RunOnceScheduler;
private pendingFoldersToAdd: IAddFoldersRequest[];
constructor(
shellContainer: HTMLElement,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ -96,6 +100,10 @@ export class ElectronWindow extends Themable {
this.touchBarUpdater = new RunOnceScheduler(() => this.doSetupTouchbar(), 300);
this.toUnbind.push(this.touchBarUpdater);
this.pendingFoldersToAdd = [];
this.addFoldersScheduler = new RunOnceScheduler(() => this.doAddFolders(), 100);
this.toUnbind.push(this.addFoldersScheduler);
this.registerListeners();
this.create();
}
@ -180,7 +188,7 @@ export class ElectronWindow extends Themable {
ipc.on('vscode:openFiles', (_event: any, request: IOpenFileRequest) => this.onOpenFiles(request));
// Support addFolders event if we have a workspace opened
ipc.on('vscode:addFolders', (_event: any, request: IAddFoldersRequest) => this.onAddFolders(request));
ipc.on('vscode:addFolders', (_event: any, request: IAddFoldersRequest) => this.onAddFoldersRequest(request));
// Message support
ipc.on('vscode:showInfoMessage', (_event: any, message: string) => {
@ -234,8 +242,13 @@ export class ElectronWindow extends Themable {
browser.setAccessibilitySupport(accessibilitySupportEnabled ? platform.AccessibilitySupport.Enabled : platform.AccessibilitySupport.Disabled);
});
// Configuration changes
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => this.onDidUpdateConfiguration(e)));
// Zoom level changes
this.updateWindowZoomLevel();
this.toUnbind.push(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('window.zoomLevel')) {
this.updateWindowZoomLevel();
}
}));
// Context menu support in input/textarea
window.document.addEventListener('contextmenu', e => this.onContextMenu(e));
@ -256,10 +269,7 @@ export class ElectronWindow extends Themable {
}
}
private onDidUpdateConfiguration(event: IConfigurationChangeEvent): void {
if (!event.affectsConfiguration('window.zoomLevel')) {
return;
}
private updateWindowZoomLevel(): void {
const windowConfig: IWindowsConfiguration = this.configurationService.getValue<IWindowsConfiguration>();
@ -394,8 +404,25 @@ export class ElectronWindow extends Themable {
});
}
private onAddFolders(request: IAddFoldersRequest): void {
const foldersToAdd = request.foldersToAdd.map(folderToAdd => ({ uri: URI.file(folderToAdd.filePath) }));
private onAddFoldersRequest(request: IAddFoldersRequest): void {
// Buffer all pending requests
this.pendingFoldersToAdd.push(request);
// Delay the adding of folders a bit to buffer in case more requests are coming
if (!this.addFoldersScheduler.isScheduled()) {
this.addFoldersScheduler.schedule();
}
}
private doAddFolders(): void {
const foldersToAdd: IWorkspaceFolderCreationData[] = [];
this.pendingFoldersToAdd.forEach(request => {
foldersToAdd.push(...request.foldersToAdd.map(folderToAdd => ({ uri: URI.file(folderToAdd.filePath) })));
});
this.pendingFoldersToAdd = [];
this.workspaceEditingService.addFolders(foldersToAdd).done(null, errors.onUnexpectedError);
}

View file

@ -44,7 +44,6 @@ import { Widget } from 'vs/base/browser/ui/widget';
import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer, FolderSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { FoldingController } from 'vs/editor/contrib/folding/folding';
import { FindController } from 'vs/editor/contrib/find/findController';
@ -697,77 +696,6 @@ class SideBySidePreferencesWidget extends Widget {
}
}
export class EditableSettingsEditor extends BaseTextEditor {
public static ID: string = 'workbench.editor.settingsEditor';
private modelDisposables: IDisposable[] = [];
private saveDelayer: Delayer<void>;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IThemeService themeService: IThemeService,
@IPreferencesService private preferencesService: IPreferencesService,
@ITextFileService textFileService: ITextFileService,
@IEditorGroupService editorGroupService: IEditorGroupService
) {
super(EditableSettingsEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorGroupService);
this._register({ dispose: () => dispose(this.modelDisposables) });
this.saveDelayer = new Delayer<void>(1000);
}
protected createEditor(parent: Builder): void {
super.createEditor(parent);
const codeEditor = getCodeEditor(this);
if (codeEditor) {
this._register(codeEditor.onDidChangeModel(() => this.onDidModelChange()));
}
}
protected getAriaLabel(): string {
const input = this.input;
const inputName = input && input.getName();
let ariaLabel: string;
if (inputName) {
ariaLabel = nls.localize('fileEditorWithInputAriaLabel', "{0}. Text file editor.", inputName);
} else {
ariaLabel = nls.localize('fileEditorAriaLabel', "Text file editor.");
}
return ariaLabel;
}
setInput(input: EditorInput, options: EditorOptions): TPromise<void> {
return super.setInput(input, options)
.then(() => this.input.resolve()
.then(editorModel => editorModel.load())
.then(editorModel => this.getControl().setModel((<ResourceEditorModel>editorModel).textEditorModel)));
}
clearInput(): void {
this.modelDisposables = dispose(this.modelDisposables);
super.clearInput();
}
private onDidModelChange(): void {
this.modelDisposables = dispose(this.modelDisposables);
const model = getCodeEditor(this).getModel();
if (model) {
this.preferencesService.createPreferencesEditorModel(model.uri)
.then(preferencesEditorModel => {
const settingsEditorModel = <SettingsEditorModel>preferencesEditorModel;
this.modelDisposables.push(settingsEditorModel);
this.modelDisposables.push(model.onDidChangeContent(() => this.saveDelayer.trigger(() => settingsEditorModel.save())));
});
}
}
}
export class DefaultPreferencesEditor extends BaseTextEditor {
public static ID: string = 'workbench.editor.defaultPreferences';

View file

@ -270,14 +270,12 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor));
this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, this.settingHighlighter));
this.feedbackWidgetRenderer = this._register(instantiationService.createInstance(FeedbackWidgetRenderer, editor));
const parenthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel));
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, parenthesisHidingRenderer]));
this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e)));
const parenthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups));
const hiddenAreasProviders = [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, parenthesisHidingRenderer];
this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, hiddenAreasProviders));
this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render()));
this._register(preferencesModel.onDidChangeGroups(() => this.render()));
this.onTriggeredFuzzy = this.settingsHeaderRenderer.onClick;
}
@ -295,7 +293,6 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR
this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups);
this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups, this._associatedPreferencesModel);
this.feedbackWidgetRenderer.render(null);
this.hiddenAreasRenderer.render();
this.settingHighlighter.clear(true);
this.settingsGroupTitleRenderer.showGroup(0);
this.hiddenAreasRenderer.render();
@ -379,7 +376,7 @@ export interface HiddenAreasProvider {
export class StaticContentHidingRenderer extends Disposable implements HiddenAreasProvider {
constructor(private editor: ICodeEditor, private settingsGroups: ISettingsGroup[]
constructor(private editor: ICodeEditor, private settingsEditorModel: ISettingsEditorModel
) {
super();
}
@ -388,7 +385,8 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
const model = this.editor.getModel();
// Hide extra chars for "search results" and "commonly used" groups
const lastGroup = tail(this.settingsGroups);
const settingsGroups = this.settingsEditorModel.settingsGroups;
const lastGroup = tail(settingsGroups);
return [
{
startLineNumber: 1,
@ -397,10 +395,10 @@ export class StaticContentHidingRenderer extends Disposable implements HiddenAre
endColumn: model.getLineMaxColumn(2)
},
{
startLineNumber: this.settingsGroups[0].range.endLineNumber + 1,
startColumn: model.getLineMinColumn(this.settingsGroups[0].range.endLineNumber + 1),
endLineNumber: this.settingsGroups[0].range.endLineNumber + 4,
endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4)
startLineNumber: settingsGroups[0].range.endLineNumber + 1,
startColumn: model.getLineMinColumn(settingsGroups[0].range.endLineNumber + 1),
endLineNumber: settingsGroups[0].range.endLineNumber + 4,
endColumn: model.getLineMaxColumn(settingsGroups[0].range.endLineNumber + 4)
},
{
startLineNumber: lastGroup.range.endLineNumber + 1,

View file

@ -21,11 +21,10 @@ import { IModel } from 'vs/editor/common/editorCommon';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/parts/preferences/common/preferences';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettingsModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettings, WorkspaceConfigurationEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor';
import { KeybindingsEditorInput } from 'vs/workbench/parts/preferences/browser/keybindingsEditor';
@ -53,9 +52,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic
private _onDispose: Emitter<void> = new Emitter<void>();
private _defaultSettingsUriCounter = 0;
private _defaultSettingsContentModel: DefaultSettingsModel;
private _defaultSettingsContentModel: DefaultSettings;
private _defaultResourceSettingsUriCounter = 0;
private _defaultResourceSettingsContentModel: DefaultSettingsModel;
private _defaultResourceSettingsContentModel: DefaultSettings;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ -68,7 +67,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic
@IEnvironmentService private environmentService: IEnvironmentService,
@ITelemetryService private telemetryService: ITelemetryService,
@ITextModelService private textModelResolverService: ITextModelService,
@IExtensionService private extensionService: IExtensionService,
@IKeybindingService keybindingService: IKeybindingService,
@IModelService private modelService: IModelService,
@IJSONEditingService private jsonEditingService: IJSONEditingService,
@ -110,14 +108,31 @@ export class PreferencesService extends Disposable implements IPreferencesServic
resolveModel(uri: URI): TPromise<IModel> {
if (this.isDefaultSettingsResource(uri) || this.isDefaultResourceSettingsResource(uri)) {
return this.extensionService.onReady()
.then(() => {
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const settingsModel = this.getDefaultSettingsModel(scope);
const mode = this.modeService.getOrCreateMode('json');
const model = this._register(this.modelService.createModel(settingsModel.content, mode, uri));
return model;
});
const scope = this.isDefaultSettingsResource(uri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
const mode = this.modeService.getOrCreateMode('json');
const model = this._register(this.modelService.createModel('', mode, uri));
let defaultSettings: DefaultSettings;
this.configurationService.onDidChangeConfiguration(e => {
if (e.source === ConfigurationTarget.DEFAULT) {
const model = this.modelService.getModel(uri);
if (!model) {
// model has not been given out => nothing to do
return;
}
defaultSettings = this.getDefaultSettings(scope);
this.modelService.updateModel(model, defaultSettings.parse());
}
});
// Check if Default settings is already created and updated in above promise
if (!defaultSettings) {
defaultSettings = this.getDefaultSettings(scope);
this.modelService.updateModel(model, defaultSettings.parse());
}
return TPromise.as(model);
}
if (this.defaultKeybindingsResource.toString() === uri.toString()) {
@ -288,20 +303,20 @@ export class PreferencesService extends Disposable implements IPreferencesServic
return this.textModelResolverService.createModelReference(defaultSettingsUri)
.then(reference => {
const scope = this.isDefaultSettingsResource(defaultSettingsUri) ? ConfigurationScope.WINDOW : ConfigurationScope.RESOURCE;
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettingsModel(scope).settingsGroups);
return this.instantiationService.createInstance(DefaultSettingsEditorModel, defaultSettingsUri, reference, scope, this.getDefaultSettings(scope));
});
}
private getDefaultSettingsModel(scope: ConfigurationScope): DefaultSettingsModel {
private getDefaultSettings(scope: ConfigurationScope): DefaultSettings {
switch (scope) {
case ConfigurationScope.WINDOW:
if (!this._defaultSettingsContentModel) {
this._defaultSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
this._defaultSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultSettingsContentModel;
case ConfigurationScope.RESOURCE:
if (!this._defaultResourceSettingsContentModel) {
this._defaultResourceSettingsContentModel = new DefaultSettingsModel(this.getMostCommonlyUsedSettings(), scope);
this._defaultResourceSettingsContentModel = new DefaultSettings(this.getMostCommonlyUsedSettings(), scope);
}
return this._defaultResourceSettingsContentModel;
}

View file

@ -8,7 +8,7 @@ import { assign } from 'vs/base/common/objects';
import { tail } from 'vs/base/common/arrays';
import URI from 'vs/base/common/uri';
import { IReference } from 'vs/base/common/lifecycle';
import Event from 'vs/base/common/event';
import Event, { Emitter } from 'vs/base/common/event';
import { Registry } from 'vs/platform/registry/common/platform';
import { visit, JSONVisitor } from 'vs/base/common/json';
import { IModel } from 'vs/editor/common/editorCommon';
@ -499,7 +499,8 @@ export class WorkspaceConfigModel extends SettingsEditorModel implements ISettin
}
}
export class DefaultSettingsModel {
export class DefaultSettings {
private _allSettingsGroups: ISettingsGroup[];
private _content: string;
@ -525,13 +526,14 @@ export class DefaultSettingsModel {
return this._allSettingsGroups;
}
private parse() {
parse(): string {
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations().slice();
const settingsGroups = this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes).reduce((result, config, index, array) => this.parseConfig(config, result, array), []));
this.initAllSettingsMap(settingsGroups);
const mostCommonlyUsed = this.getMostCommonlyUsedSettings(settingsGroups);
this._allSettingsGroups = [mostCommonlyUsed, ...settingsGroups];
this._content = this.toContent(mostCommonlyUsed, settingsGroups);
return this._content;
}
private initAllSettingsMap(allSettingsGroups: ISettingsGroup[]): void {
@ -678,15 +680,26 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
private _settingsByName: Map<string, ISetting>;
private _mostRelevantLineOffset: number;
private _settingsGroups: ISettingsGroup[];
private _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
constructor(
private _uri: URI,
reference: IReference<ITextEditorModel>,
readonly configurationScope: ConfigurationScope,
readonly settingsGroups: ISettingsGroup[]
defaultSettings: DefaultSettings
) {
super();
this._settingsGroups = defaultSettings.settingsGroups;
this._model = reference.object.textEditorModel;
this._register(this.onDispose(() => reference.dispose()));
this._register(this._model.onDidChangeContent(() => {
this._settingsGroups = defaultSettings.settingsGroups;
this._onDidChangeGroups.fire();
}));
this.initAllSettingsMap();
this._mostRelevantLineOffset = tail(this.settingsGroups).range.endLineNumber + 2;
@ -696,6 +709,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
return this._uri;
}
public get settingsGroups(): ISettingsGroup[] {
return this._settingsGroups;
}
public filterSettings(filter: string, groupFilter: IGroupFilter, settingFilter: ISettingFilter, mostRelevantSettings?: string[]): IFilterResult {
if (mostRelevantSettings) {
const mostRelevantGroup = this.renderMostRelevantSettings(mostRelevantSettings);

View file

@ -20,7 +20,7 @@ import { getCodeEditor } from 'vs/editor/browser/services/codeEditorService';
import { IRange } from 'vs/editor/common/core/range';
import { overviewRulerRangeHighlight } from 'vs/editor/common/view/editorColorRegistry';
import { themeColorFromId } from 'vs/platform/theme/common/themeService';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IEditorOptions, RenderLineNumbersType } from 'vs/editor/common/config/editorOptions';
export const GOTO_LINE_PREFIX = ':';
@ -43,7 +43,7 @@ export class GotoLineAction extends QuickOpenAction {
if (editor) {
const config = editor.getConfiguration();
if (config.viewInfo.renderLineNumbers && config.viewInfo.renderRelativeLineNumbers) {
if (config.viewInfo.renderLineNumbers === RenderLineNumbersType.Relative) {
editor.updateOptions({
lineNumbers: 'on'
});

View file

@ -70,6 +70,33 @@ function etag(arg1: any, arg2?: any): string {
return `"${crypto.createHash('sha1').update(String(size) + String(mtime)).digest('hex')}"`;
}
class BufferPool {
static _64K = new BufferPool(64 * 1024, 5);
constructor(
readonly bufferSize: number,
private readonly _capacity: number,
private readonly _free: Buffer[] = [],
) {
//
}
acquire(): Buffer {
if (this._free.length === 0) {
return Buffer.allocUnsafe(this.bufferSize);
} else {
return this._free.shift();
}
}
release(buf: Buffer): void {
if (this._free.length <= this._capacity) {
this._free.push(buf);
}
}
}
export class FileService implements IFileService {
public _serviceBrand: any;
@ -324,10 +351,11 @@ export class FileService implements IFileService {
//#region data stream
private static chunkSize = 64 * 1024;
private static chunkBuffer = Buffer.allocUnsafe(FileService.chunkSize);
private resolveFileData(resource: uri, options: IResolveContentOptions, token: CancellationToken): Thenable<IContentData> {
const chunkBuffer = BufferPool._64K.acquire();
const result: IContentData = {
encoding: undefined,
stream: undefined,
@ -371,10 +399,14 @@ export class FileService implements IFileService {
if (decoder) {
decoder.end();
}
// return the shared buffer
BufferPool._64K.release(chunkBuffer);
if (fd) {
fs.close(fd, err => {
if (err) {
this.options.errorLogger(err.toString());
this.options.errorLogger(`resolveFileData#close(): ${err.toString()}`);
}
});
}
@ -382,21 +414,22 @@ export class FileService implements IFileService {
const handleChunk = (bytesRead) => {
if (token.isCancellationRequested) {
// cancellation
// cancellation -> finish
finish(new Error('cancelled'));
} else if (bytesRead < FileService.chunkSize) {
// done, write rest, end
decoder.write(FileService.chunkBuffer.slice(0, bytesRead), finish);
} else if (bytesRead === 0) {
// no more data -> finish
finish();
} else if (bytesRead < chunkBuffer.length) {
// write the sub-part of data we received -> repeat
decoder.write(chunkBuffer.slice(0, bytesRead), readChunk);
} else {
// read, write, repeat
decoder.write(FileService.chunkBuffer, readChunk);
// write all data we received -> repeat
decoder.write(chunkBuffer, readChunk);
}
};
const readChunk = () => {
fs.read(fd, FileService.chunkBuffer, 0, FileService.chunkSize, null, (err, bytesRead) => {
fs.read(fd, chunkBuffer, 0, chunkBuffer.length, null, (err, bytesRead) => {
totalBytesRead += bytesRead;
if (totalBytesRead > MAX_FILE_SIZE) {
@ -417,7 +450,7 @@ export class FileService implements IFileService {
// when receiving the first chunk of data we need to create the
// decoding stream which is then used to drive the string stream.
Promise.resolve(detectMimeAndEncodingFromBuffer(
{ buffer: FileService.chunkBuffer, bytesRead },
{ buffer: chunkBuffer, bytesRead },
options && options.autoGuessEncoding || this.configuredAutoGuessEncoding(resource)
)).then(value => {

View file

@ -635,6 +635,23 @@ suite('FileService', () => {
}, error => onError(error, done));
});
test('Files are intermingled #38331', function () {
let resource1 = uri.file(path.join(testDir, 'lorem.txt'));
let resource2 = uri.file(path.join(testDir, 'some_utf16le.css'));
let value1: string;
let value2: string;
// load in sequence and keep data
return service.resolveContent(resource1).then(c => value1 = c.value).then(() => {
return service.resolveContent(resource2).then(c => value2 = c.value);
}).then(() => {
// load in parallel in expect the same result
return TPromise.join([
service.resolveContent(resource1).then(c => assert.equal(c.value, value1)),
service.resolveContent(resource2).then(c => assert.equal(c.value, value2))
]);
});
});
test('resolveContent - FILE_IS_BINARY', function (done: () => void) {
const resource = uri.file(path.join(testDir, 'binary.txt'));

View file

@ -581,7 +581,7 @@ if (OS === OperatingSystem.Macintosh || OS === OperatingSystem.Linux) {
'type': 'string',
'enum': ['code', 'keyCode'],
'default': 'code',
'description': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `keydown.code` (recommended) or `keydown.keyCode`.")
'description': nls.localize('dispatch', "Controls the dispatching logic for key presses to use either `code` (recommended) or `keyCode`.")
}
}
};

View file

@ -162,7 +162,7 @@ export abstract class TextFileService implements ITextFileService {
return this.confirmBeforeShutdown();
}, errors => {
const firstError = errors[0];
this.messageService.show(Severity.Error, nls.localize('files.backup.failSave', "Files could not be backed up (Error: {0}), try saving your files to exit.", firstError.message));
this.messageService.show(Severity.Error, nls.localize('files.backup.failSave', "Files that are dirty could not be written to the backup location (Error: {0}). Try saving your files first and then exit.", firstError.message));
return true; // veto, the backups failed
});