Merge from master

This commit is contained in:
Raj Musuku
2019-02-21 17:56:04 -08:00
parent 5a146e34fa
commit 666ae11639
11482 changed files with 119352 additions and 255574 deletions

View File

@@ -3,26 +3,25 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IRequestHandler } from 'vs/base/common/worker/simpleWorker';
import { Range, IRange } from 'vs/editor/common/core/range';
import { DiffComputer } from 'vs/editor/common/diff/diffComputer';
import { mergeSort } from 'vs/base/common/arrays';
import { stringDiff } from 'vs/base/common/diff/diff';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from 'vs/editor/common/model/mirrorTextModel';
import { IInplaceReplaceSupportResult, ILink, ISuggestResult, ISuggestion, TextEdit } from 'vs/editor/common/modes';
import { computeLinks, ILinkComputerTarget } from 'vs/editor/common/modes/linkComputer';
import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport';
import { getWordAtText, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
import { IWordAtPosition, EndOfLineSequence } from 'vs/editor/common/model';
import { FIN, Iterator, IteratorResult } from 'vs/base/common/iterator';
import { IDisposable } from 'vs/base/common/lifecycle';
import { globals } from 'vs/base/common/platform';
import { Iterator } from 'vs/base/common/iterator';
import { URI } from 'vs/base/common/uri';
import { IRequestHandler } from 'vs/base/common/worker/simpleWorker';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange, Range } from 'vs/editor/common/core/range';
import { DiffComputer } from 'vs/editor/common/diff/diffComputer';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, IWordAtPosition } from 'vs/editor/common/model';
import { IModelChangedEvent, MirrorTextModel as BaseMirrorModel } from 'vs/editor/common/model/mirrorTextModel';
import { ensureValidWordDefinition, getWordAtText } from 'vs/editor/common/model/wordHelper';
import { CompletionItem, CompletionItemKind, CompletionList, IInplaceReplaceSupportResult, ILink, TextEdit } from 'vs/editor/common/modes';
import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkComputer';
import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport';
import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService';
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
export interface IMirrorModel {
readonly uri: URI;
@@ -59,10 +58,11 @@ export interface ICommonModel extends ILinkComputerTarget, IMirrorModel {
getLinesContent(): string[];
getLineCount(): number;
getLineContent(lineNumber: number): string;
getLineWords(lineNumber: number, wordDefinition: RegExp): IWordAtPosition[];
createWordIterator(wordDefinition: RegExp): Iterator<string>;
getWordUntilPosition(position: IPosition, wordDefinition: RegExp): IWordAtPosition;
getValueInRange(range: IRange): string;
getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range;
getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range | null;
offsetAt(position: IPosition): number;
positionAt(offset: number): IPosition;
}
@@ -115,7 +115,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
return this._lines[lineNumber - 1];
}
public getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range {
public getWordAtPosition(position: IPosition, wordDefinition: RegExp): Range | null {
let wordAtText = getWordAtText(
position.column,
@@ -148,24 +148,25 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
}
public createWordIterator(wordDefinition: RegExp): Iterator<string> {
let obj = {
done: false,
value: ''
};
let obj: { done: false; value: string; };
let lineNumber = 0;
let lineText: string;
let wordRangesIdx = 0;
let wordRanges: IWordRange[] = [];
let next = (): { done: boolean; value: string } => {
let next = (): IteratorResult<string> => {
if (wordRangesIdx < wordRanges.length) {
obj.done = false;
obj.value = lineText.substring(wordRanges[wordRangesIdx].start, wordRanges[wordRangesIdx].end);
const value = lineText.substring(wordRanges[wordRangesIdx].start, wordRanges[wordRangesIdx].end);
wordRangesIdx += 1;
if (!obj) {
obj = { done: false, value: value };
} else {
obj.value = value;
}
return obj;
} else if (lineNumber >= this._lines.length) {
obj.done = true;
obj.value = undefined;
return FIN;
} else {
lineText = this._lines[lineNumber];
@@ -174,15 +175,27 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
lineNumber += 1;
return next();
}
return obj;
};
return { next };
}
public getLineWords(lineNumber: number, wordDefinition: RegExp): IWordAtPosition[] {
let content = this._lines[lineNumber - 1];
let ranges = this._wordenize(content, wordDefinition);
let words: IWordAtPosition[] = [];
for (const range of ranges) {
words.push({
word: content.substring(range.start, range.end),
startColumn: range.start + 1,
endColumn: range.end + 1
});
}
return words;
}
private _wordenize(content: string, wordDefinition: RegExp): IWordRange[] {
const result: IWordRange[] = [];
let match: RegExpExecArray;
let match: RegExpExecArray | null;
wordDefinition.lastIndex = 0; // reset lastIndex just to be sure
@@ -220,7 +233,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
public offsetAt(position: IPosition): number {
position = this._validatePosition(position);
this._ensureLineStarts();
return this._lineStarts.getAccumulatedValue(position.lineNumber - 2) + (position.column - 1);
return this._lineStarts!.getAccumulatedValue(position.lineNumber - 2) + (position.column - 1);
}
public positionAt(offset: number): IPosition {
@@ -228,7 +241,7 @@ class MirrorModel extends BaseMirrorModel implements ICommonModel {
offset = Math.max(0, offset);
this._ensureLineStarts();
let out = this._lineStarts.getIndexOf(offset);
let out = this._lineStarts!.getIndexOf(offset);
let lineLength = this._lines[out.index].length;
// Ensure we return a valid position
@@ -309,10 +322,10 @@ declare var require: any;
* @internal
*/
export abstract class BaseEditorSimpleWorker {
private _foreignModuleFactory: IForeignModuleFactory;
private _foreignModuleFactory: IForeignModuleFactory | null;
private _foreignModule: any;
constructor(foreignModuleFactory: IForeignModuleFactory) {
constructor(foreignModuleFactory: IForeignModuleFactory | null) {
this._foreignModuleFactory = foreignModuleFactory;
this._foreignModule = null;
}
@@ -322,29 +335,51 @@ export abstract class BaseEditorSimpleWorker {
// ---- BEGIN diff --------------------------------------------------------------------------
public computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): TPromise<editorCommon.ILineChange[]> {
let original = this._getModel(originalUrl);
let modified = this._getModel(modifiedUrl);
public computeDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
const original = this._getModel(originalUrl);
const modified = this._getModel(modifiedUrl);
if (!original || !modified) {
return null;
return Promise.resolve(null);
}
let originalLines = original.getLinesContent();
let modifiedLines = modified.getLinesContent();
let diffComputer = new DiffComputer(originalLines, modifiedLines, {
const originalLines = original.getLinesContent();
const modifiedLines = modified.getLinesContent();
const diffComputer = new DiffComputer(originalLines, modifiedLines, {
shouldComputeCharChanges: true,
shouldPostProcessCharChanges: true,
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
shouldMakePrettyDiff: true
});
return TPromise.as(diffComputer.computeDiff());
const changes = diffComputer.computeDiff();
let identical = (changes.length > 0 ? false : this._modelsAreIdentical(original, modified));
return Promise.resolve({
identical: identical,
changes: changes
});
}
public computeDirtyDiff(originalUrl: string, modifiedUrl: string, ignoreTrimWhitespace: boolean): TPromise<editorCommon.IChange[]> {
private _modelsAreIdentical(original: ICommonModel, modified: ICommonModel): boolean {
const originalLineCount = original.getLineCount();
const modifiedLineCount = modified.getLineCount();
if (originalLineCount !== modifiedLineCount) {
return false;
}
for (let line = 1; line <= originalLineCount; line++) {
const originalLine = original.getLineContent(line);
const modifiedLine = modified.getLineContent(line);
if (originalLine !== modifiedLine) {
return false;
}
}
return true;
}
public 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 null;
return Promise.resolve(null);
}
let originalLines = original.getLinesContent();
@@ -355,7 +390,7 @@ export abstract class BaseEditorSimpleWorker {
shouldIgnoreTrimWhitespace: ignoreTrimWhitespace,
shouldMakePrettyDiff: true
});
return TPromise.as(diffComputer.computeDiff());
return Promise.resolve(diffComputer.computeDiff());
}
// ---- END diff --------------------------------------------------------------------------
@@ -365,14 +400,24 @@ export abstract class BaseEditorSimpleWorker {
private static readonly _diffLimit = 10000;
public computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[]): TPromise<TextEdit[]> {
public computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[]): Promise<TextEdit[]> {
const model = this._getModel(modelUrl);
if (!model) {
return TPromise.as(edits);
return Promise.resolve(edits);
}
const result: TextEdit[] = [];
let lastEol: EndOfLineSequence;
let lastEol: EndOfLineSequence | undefined = undefined;
edits = mergeSort(edits, (a, b) => {
if (a.range && b.range) {
return Range.compareRangesUsingStarts(a.range, b.range);
}
// eol only changes should go to the end
let aRng = a.range ? 0 : 1;
let bRng = b.range ? 0 : 1;
return aRng - bRng;
});
for (let { range, text, eol } of edits) {
@@ -380,13 +425,13 @@ export abstract class BaseEditorSimpleWorker {
lastEol = eol;
}
if (!range) {
// eol-change only
if (Range.isEmpty(range) && !text) {
// empty change
continue;
}
const original = model.getValueInRange(range);
text = text.replace(/\r\n|\n|\r/g, model.eol);
text = text!.replace(/\r\n|\n|\r/g, model.eol);
if (original === text) {
// noop
@@ -418,72 +463,105 @@ export abstract class BaseEditorSimpleWorker {
}
if (typeof lastEol === 'number') {
result.push({ eol: lastEol, text: undefined, range: undefined });
result.push({ eol: lastEol, text: '', range: { startLineNumber: 0, startColumn: 0, endLineNumber: 0, endColumn: 0 } });
}
return TPromise.as(result);
return Promise.resolve(result);
}
// ---- END minimal edits ---------------------------------------------------------------
public computeLinks(modelUrl: string): TPromise<ILink[]> {
public computeLinks(modelUrl: string): Promise<ILink[] | null> {
let model = this._getModel(modelUrl);
if (!model) {
return null;
return Promise.resolve(null);
}
return TPromise.as(computeLinks(model));
return Promise.resolve(computeLinks(model));
}
// ---- BEGIN suggest --------------------------------------------------------------------------
private static readonly _suggestionsLimit = 10000;
public textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): TPromise<ISuggestResult> {
public textualSuggest(modelUrl: string, position: IPosition, wordDef: string, wordDefFlags: string): Promise<CompletionList | null> {
const model = this._getModel(modelUrl);
if (model) {
const suggestions: ISuggestion[] = [];
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
const currentWord = model.getWordUntilPosition(position, wordDefRegExp).word;
if (!model) {
return Promise.resolve(null);
}
const seen: Record<string, boolean> = Object.create(null);
seen[currentWord] = true;
const suggestions: CompletionItem[] = [];
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
const currentWord = model.getWordUntilPosition(position, wordDefRegExp);
for (
let iter = model.createWordIterator(wordDefRegExp), e = iter.next();
!e.done && suggestions.length <= BaseEditorSimpleWorker._suggestionsLimit;
e = iter.next()
) {
const word = e.value;
if (seen[word]) {
continue;
}
seen[word] = true;
if (!isNaN(Number(word))) {
continue;
}
const seen: Record<string, boolean> = Object.create(null);
seen[currentWord.word] = true;
suggestions.push({
type: 'text',
label: word,
insertText: word,
noAutoAccept: true,
overwriteBefore: currentWord.length
});
for (
let iter = model.createWordIterator(wordDefRegExp), e = iter.next();
!e.done && suggestions.length <= BaseEditorSimpleWorker._suggestionsLimit;
e = iter.next()
) {
const word = e.value;
if (seen[word]) {
continue;
}
seen[word] = true;
if (!isNaN(Number(word))) {
continue;
}
return TPromise.as({ suggestions });
suggestions.push({
kind: CompletionItemKind.Text,
label: word,
insertText: word,
range: { startLineNumber: position.lineNumber, startColumn: currentWord.startColumn, endLineNumber: position.lineNumber, endColumn: currentWord.endColumn }
});
}
return undefined;
return Promise.resolve({ suggestions });
}
// ---- END suggest --------------------------------------------------------------------------
public navigateValueSet(modelUrl: string, range: IRange, up: boolean, wordDef: string, wordDefFlags: string): TPromise<IInplaceReplaceSupportResult> {
//#region -- word ranges --
computeWordRanges(modelUrl: string, range: IRange, wordDef: string, wordDefFlags: string): Promise<{ [word: string]: IRange[] }> {
let model = this._getModel(modelUrl);
if (!model) {
return null;
return Promise.resolve(Object.create(null));
}
const wordDefRegExp = new RegExp(wordDef, wordDefFlags);
const result: { [word: string]: IRange[] } = Object.create(null);
for (let line = range.startLineNumber; line < range.endLineNumber; line++) {
let words = model.getLineWords(line, wordDefRegExp);
for (const word of words) {
if (!isNaN(Number(word.word))) {
continue;
}
let array = result[word.word];
if (!array) {
array = [];
result[word.word] = array;
}
array.push({
startLineNumber: line,
startColumn: word.startColumn,
endLineNumber: line,
endColumn: word.endColumn
});
}
}
return Promise.resolve(result);
}
//#endregion
public 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);
}
let wordDefRegExp = new RegExp(wordDef, wordDefFlags);
@@ -500,18 +578,17 @@ export abstract class BaseEditorSimpleWorker {
let selectionText = model.getValueInRange(range);
let wordRange = model.getWordAtPosition({ lineNumber: range.startLineNumber, column: range.startColumn }, wordDefRegExp);
let word: string = null;
if (wordRange !== null) {
word = model.getValueInRange(wordRange);
if (!wordRange) {
return Promise.resolve(null);
}
let word = model.getValueInRange(wordRange);
let result = BasicInplaceReplace.INSTANCE.navigateValueSet(range, selectionText, wordRange, word, up);
return TPromise.as(result);
return Promise.resolve(result);
}
// ---- BEGIN foreign module support --------------------------------------------------------------------------
public loadForeignModule(moduleId: string, createData: any): TPromise<string[]> {
public loadForeignModule(moduleId: string, createData: any): Promise<string[]> {
let ctx: IWorkerContext = {
getMirrorModels: (): IMirrorModel[] => {
return this._getModels();
@@ -527,9 +604,10 @@ export abstract class BaseEditorSimpleWorker {
methods.push(prop);
}
}
return TPromise.as(methods);
return Promise.resolve(methods);
}
return new TPromise<any>((c, e) => {
// ESM-comment-begin
return new Promise<any>((resolve, reject) => {
require([moduleId], (foreignModule: { create: IForeignModuleFactory }) => {
this._foreignModule = foreignModule.create(ctx, createData);
@@ -540,22 +618,27 @@ export abstract class BaseEditorSimpleWorker {
}
}
c(methods);
resolve(methods);
}, e);
}, reject);
});
// ESM-comment-end
// ESM-uncomment-begin
// return Promise.reject(new Error(`Unexpected usage`));
// ESM-uncomment-end
}
// foreign method request
public fmr(method: string, args: any[]): TPromise<any> {
public fmr(method: string, args: any[]): Promise<any> {
if (!this._foreignModule || typeof this._foreignModule[method] !== 'function') {
return TPromise.wrapError(new Error('Missing requestHandler or method: ' + method));
return Promise.reject(new Error('Missing requestHandler or method: ' + method));
}
try {
return TPromise.as(this._foreignModule[method].apply(this._foreignModule, args));
return Promise.resolve(this._foreignModule[method].apply(this._foreignModule, args));
} catch (e) {
return TPromise.wrapError(e);
return Promise.reject(e);
}
}
@@ -570,7 +653,7 @@ export class EditorSimpleWorkerImpl extends BaseEditorSimpleWorker implements IR
private _models: { [uri: string]: MirrorModel; };
constructor(foreignModuleFactory: IForeignModuleFactory) {
constructor(foreignModuleFactory: IForeignModuleFactory | null) {
super(foreignModuleFactory);
this._models = Object.create(null);
}

View File

@@ -2,29 +2,35 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IRange } from 'vs/editor/common/core/range';
import { IChange, ILineChange } from 'vs/editor/common/editorCommon';
import { IInplaceReplaceSupportResult, TextEdit } from 'vs/editor/common/modes';
import { IRange } from 'vs/editor/common/core/range';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ID_EDITOR_WORKER_SERVICE = 'editorWorkerService';
export const IEditorWorkerService = createDecorator<IEditorWorkerService>(ID_EDITOR_WORKER_SERVICE);
export interface IDiffComputationResult {
identical: boolean;
changes: ILineChange[];
}
export interface IEditorWorkerService {
_serviceBrand: any;
canComputeDiff(original: URI, modified: URI): boolean;
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<ILineChange[]>;
computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null>;
canComputeDirtyDiff(original: URI, modified: URI): boolean;
computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<IChange[]>;
computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IChange[] | null>;
computeMoreMinimalEdits(resource: URI, edits: TextEdit[]): TPromise<TextEdit[]>;
computeMoreMinimalEdits(resource: URI, edits: TextEdit[]): Promise<TextEdit[]>;
canComputeWordRanges(resource: URI): boolean;
computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null>;
canNavigateValueSet(resource: URI): boolean;
navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise<IInplaceReplaceSupportResult>;
navigateValueSet(resource: URI, range: IRange, up: boolean): Promise<IInplaceReplaceSupportResult | null>;
}

View File

@@ -2,25 +2,23 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IntervalTimer, ShallowCancelThenPromise, wireCancellationToken } from 'vs/base/common/async';
import { IntervalTimer } from 'vs/base/common/async';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { URI } from 'vs/base/common/uri';
import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory';
import * as editorCommon from 'vs/editor/common/editorCommon';
import * as modes from 'vs/editor/common/modes';
import { Position, IPosition } from 'vs/editor/common/core/position';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IRange } from 'vs/editor/common/core/range';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import * as modes from 'vs/editor/common/modes';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker';
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
/**
* Stop syncing a model to the worker if it was not needed for 1 min.
@@ -61,12 +59,12 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
this._register(modes.LinkProviderRegistry.register('*', <modes.LinkProvider>{
provideLinks: (model, token) => {
if (!canSyncModel(this._modelService, model.uri)) {
return TPromise.as([]); // File too large
return Promise.resolve([]); // File too large
}
return wireCancellationToken(token, this._workerManager.withWorker().then(client => client.computeLinks(model.uri)));
return this._workerManager.withWorker().then(client => client.computeLinks(model.uri));
}
}));
this._register(modes.SuggestRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, this._modelService)));
this._register(modes.CompletionProviderRegistry.register('*', new WordBasedCompletionItemProvider(this._workerManager, configurationService, this._modelService)));
}
public dispose(): void {
@@ -77,7 +75,7 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified));
}
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.ILineChange[]> {
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
return this._workerManager.withWorker().then(client => client.computeDiff(original, modified, ignoreTrimWhitespace));
}
@@ -85,16 +83,16 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
return (canSyncModel(this._modelService, original) && canSyncModel(this._modelService, modified));
}
public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.IChange[]> {
public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<editorCommon.IChange[] | null> {
return this._workerManager.withWorker().then(client => client.computeDirtyDiff(original, modified, ignoreTrimWhitespace));
}
public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[]): TPromise<modes.TextEdit[]> {
public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[]): Promise<modes.TextEdit[]> {
if (!Array.isArray(edits) || edits.length === 0) {
return TPromise.as(edits);
return Promise.resolve(edits);
} else {
if (!canSyncModel(this._modelService, resource)) {
return TPromise.as(edits); // File too large
return Promise.resolve(edits); // File too large
}
return this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits));
}
@@ -104,12 +102,20 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
return (canSyncModel(this._modelService, resource));
}
public navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise<modes.IInplaceReplaceSupportResult> {
public navigateValueSet(resource: URI, range: IRange, up: boolean): Promise<modes.IInplaceReplaceSupportResult | null> {
return this._workerManager.withWorker().then(client => client.navigateValueSet(resource, range, up));
}
canComputeWordRanges(resource: URI): boolean {
return canSyncModel(this._modelService, resource);
}
computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> {
return this._workerManager.withWorker().then(client => client.computeWordRanges(resource, range));
}
}
class WordBasedCompletionItemProvider implements modes.ISuggestSupport {
class WordBasedCompletionItemProvider implements modes.CompletionItemProvider {
private readonly _workerManager: WorkerManager;
private readonly _configurationService: ITextResourceConfigurationService;
@@ -125,7 +131,7 @@ class WordBasedCompletionItemProvider implements modes.ISuggestSupport {
this._modelService = modelService;
}
provideCompletionItems(model: ITextModel, position: Position): TPromise<modes.ISuggestResult> {
provideCompletionItems(model: ITextModel, position: Position): Promise<modes.CompletionList | null> | undefined {
const { wordBasedSuggestions } = this._configurationService.getValue<IEditorOptions>(model.uri, position, 'editor');
if (!wordBasedSuggestions) {
return undefined;
@@ -140,7 +146,7 @@ class WordBasedCompletionItemProvider implements modes.ISuggestSupport {
class WorkerManager extends Disposable {
private _modelService: IModelService;
private _editorWorkerClient: EditorWorkerClient;
private _editorWorkerClient: EditorWorkerClient | null;
private _lastWorkerUsedTime: number;
constructor(modelService: IModelService) {
@@ -193,12 +199,12 @@ class WorkerManager extends Disposable {
}
}
public withWorker(): TPromise<EditorWorkerClient> {
public withWorker(): Promise<EditorWorkerClient> {
this._lastWorkerUsedTime = (new Date()).getTime();
if (!this._editorWorkerClient) {
this._editorWorkerClient = new EditorWorkerClient(this._modelService, 'editorWorkerService');
}
return TPromise.as(this._editorWorkerClient);
return Promise.resolve(this._editorWorkerClient);
}
}
@@ -301,38 +307,36 @@ class EditorModelManager extends Disposable {
}
interface IWorkerClient<T> {
getProxyObject(): TPromise<T>;
getProxyObject(): Promise<T>;
dispose(): void;
}
class SynchronousWorkerClient<T extends IDisposable> implements IWorkerClient<T> {
private _instance: T;
private _proxyObj: TPromise<T>;
private _proxyObj: Promise<T>;
constructor(instance: T) {
this._instance = instance;
this._proxyObj = TPromise.as(this._instance);
this._proxyObj = Promise.resolve(this._instance);
}
public dispose(): void {
this._instance.dispose();
this._instance = null;
this._proxyObj = null;
}
public getProxyObject(): TPromise<T> {
return new ShallowCancelThenPromise(this._proxyObj);
public getProxyObject(): Promise<T> {
return this._proxyObj;
}
}
export class EditorWorkerClient extends Disposable {
private _modelService: IModelService;
private _worker: IWorkerClient<EditorSimpleWorkerImpl>;
private _worker: IWorkerClient<EditorSimpleWorkerImpl> | null;
private _workerFactory: DefaultWorkerFactory;
private _modelManager: EditorModelManager;
private _modelManager: EditorModelManager | null;
constructor(modelService: IModelService, label: string) {
constructor(modelService: IModelService, label: string | undefined) {
super();
this._modelService = modelService;
this._workerFactory = new DefaultWorkerFactory(label);
@@ -355,12 +359,12 @@ export class EditorWorkerClient extends Disposable {
return this._worker;
}
protected _getProxy(): TPromise<EditorSimpleWorkerImpl> {
return new ShallowCancelThenPromise(this._getOrCreateWorker().getProxyObject().then(null, (err) => {
protected _getProxy(): Promise<EditorSimpleWorkerImpl> {
return this._getOrCreateWorker().getProxyObject().then(null, (err) => {
logOnceWebWorkerWarning(err);
this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl(null));
return this._getOrCreateWorker().getProxyObject();
}));
});
}
private _getOrCreateModelManager(proxy: EditorSimpleWorkerImpl): EditorModelManager {
@@ -370,38 +374,38 @@ export class EditorWorkerClient extends Disposable {
return this._modelManager;
}
protected _withSyncedResources(resources: URI[]): TPromise<EditorSimpleWorkerImpl> {
protected _withSyncedResources(resources: URI[]): Promise<EditorSimpleWorkerImpl> {
return this._getProxy().then((proxy) => {
this._getOrCreateModelManager(proxy).esureSyncedResources(resources);
return proxy;
});
}
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.ILineChange[]> {
public computeDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<IDiffComputationResult | null> {
return this._withSyncedResources([original, modified]).then(proxy => {
return proxy.computeDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): TPromise<editorCommon.IChange[]> {
public computeDirtyDiff(original: URI, modified: URI, ignoreTrimWhitespace: boolean): Promise<editorCommon.IChange[] | null> {
return this._withSyncedResources([original, modified]).then(proxy => {
return proxy.computeDirtyDiff(original.toString(), modified.toString(), ignoreTrimWhitespace);
});
}
public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[]): TPromise<modes.TextEdit[]> {
public computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[]): Promise<modes.TextEdit[]> {
return this._withSyncedResources([resource]).then(proxy => {
return proxy.computeMoreMinimalEdits(resource.toString(), edits);
});
}
public computeLinks(resource: URI): TPromise<modes.ILink[]> {
public computeLinks(resource: URI): Promise<modes.ILink[] | null> {
return this._withSyncedResources([resource]).then(proxy => {
return proxy.computeLinks(resource.toString());
});
}
public textualSuggest(resource: URI, position: IPosition): TPromise<modes.ISuggestResult> {
public textualSuggest(resource: URI, position: IPosition): Promise<modes.CompletionList | null> {
return this._withSyncedResources([resource]).then(proxy => {
let model = this._modelService.getModel(resource);
if (!model) {
@@ -414,7 +418,20 @@ export class EditorWorkerClient extends Disposable {
});
}
public navigateValueSet(resource: URI, range: IRange, up: boolean): TPromise<modes.IInplaceReplaceSupportResult> {
computeWordRanges(resource: URI, range: IRange): Promise<{ [word: string]: IRange[] } | null> {
return this._withSyncedResources([resource]).then(proxy => {
let model = this._modelService.getModel(resource);
if (!model) {
return Promise.resolve(null);
}
let wordDefRegExp = LanguageConfigurationRegistry.getWordDefinition(model.getLanguageIdentifier().id);
let wordDef = wordDefRegExp.source;
let wordDefFlags = (wordDefRegExp.global ? 'g' : '') + (wordDefRegExp.ignoreCase ? 'i' : '') + (wordDefRegExp.multiline ? 'm' : '');
return proxy.computeWordRanges(resource.toString(), range, wordDef, wordDefFlags);
});
}
public navigateValueSet(resource: URI, range: IRange, up: boolean): Promise<modes.IInplaceReplaceSupportResult | null> {
return this._withSyncedResources([resource]).then(proxy => {
let model = this._modelService.getModel(resource);
if (!model) {

View File

@@ -0,0 +1,73 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Schemas } from 'vs/base/common/network';
import { DataUri, basenameOrAuthority } from 'vs/base/common/resources';
import { URI as uri } from 'vs/base/common/uri';
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { FileKind } from 'vs/platform/files/common/files';
export function getIconClasses(modelService: IModelService, modeService: IModeService, resource: uri, fileKind?: FileKind): string[] {
// we always set these base classes even if we do not have a path
const classes = fileKind === FileKind.ROOT_FOLDER ? ['rootfolder-icon'] : fileKind === FileKind.FOLDER ? ['folder-icon'] : ['file-icon'];
if (resource) {
// Get the path and name of the resource. For data-URIs, we need to parse specially
let name: string;
let path: string;
if (resource.scheme === Schemas.data) {
const metadata = DataUri.parseMetaData(resource);
name = metadata.get(DataUri.META_DATA_LABEL);
path = name;
}
else {
name = cssEscape(basenameOrAuthority(resource).toLowerCase());
path = resource.path.toLowerCase();
}
// Folders
if (fileKind === FileKind.FOLDER) {
classes.push(`${name}-name-folder-icon`);
}
// Files
else {
// Name & Extension(s)
if (name) {
classes.push(`${name}-name-file-icon`);
const dotSegments = name.split('.');
for (let i = 1; i < dotSegments.length; i++) {
classes.push(`${dotSegments.slice(i).join('.')}-ext-file-icon`); // add each combination of all found extensions if more than one
}
classes.push(`ext-file-icon`); // extra segment to increase file-ext score
}
// Configured Language
let configuredLangId: string | null = getConfiguredLangId(modelService, resource);
configuredLangId = configuredLangId || modeService.getModeIdByFilepathOrFirstLine(path);
if (configuredLangId) {
classes.push(`${cssEscape(configuredLangId)}-lang-file-icon`);
}
}
}
return classes;
}
export function getConfiguredLangId(modelService: IModelService, resource: uri): string | null {
let configuredLangId: string | null = null;
if (resource) {
const model = modelService.getModel(resource);
if (model) {
const modeId = model.getLanguageIdentifier().language;
if (modeId && modeId !== PLAINTEXT_MODE_ID) {
configuredLangId = modeId; // only take if the mode is specific (aka no just plain text)
}
}
}
return configuredLangId;
}
export function cssEscape(val: string): string {
return val.replace(/\s/g, '\\$&'); // make sure to not introduce CSS classes from files that contain whitespace
}

View File

@@ -2,24 +2,25 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import * as mime from 'vs/base/common/mime';
import * as strings from 'vs/base/common/strings';
import { Registry } from 'vs/platform/registry/common/platform';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService';
import { URI } from 'vs/base/common/uri';
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { NULL_MODE_ID, NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import URI from 'vs/base/common/uri';
import { ModesRegistry } from 'vs/editor/common/modes/modesRegistry';
import { NULL_LANGUAGE_IDENTIFIER, NULL_MODE_ID } from 'vs/editor/common/modes/nullMode';
import { ILanguageExtensionPoint } from 'vs/editor/common/services/modeService';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
const hasOwnProperty = Object.prototype.hasOwnProperty;
export interface IResolvedLanguage {
identifier: LanguageIdentifier;
name: string;
name: string | null;
mimetypes: string[];
aliases: string[];
extensions: string[];
@@ -27,7 +28,10 @@ export interface IResolvedLanguage {
configurationFiles: URI[];
}
export class LanguagesRegistry {
export class LanguagesRegistry extends Disposable {
private readonly _onDidChange: Emitter<void> = this._register(new Emitter<void>());
public readonly onDidChange: Event<void> = this._onDidChange.event;
private _nextLanguageId: number;
private _languages: { [id: string]: IResolvedLanguage; };
@@ -40,6 +44,7 @@ export class LanguagesRegistry {
private _warnOnOverwrite: boolean;
constructor(useModesRegistry = true, warnOnOverwrite = false) {
super();
this._nextLanguageId = 1;
this._languages = {};
this._mimeTypesMap = {};
@@ -50,7 +55,7 @@ export class LanguagesRegistry {
if (useModesRegistry) {
this._registerLanguages(ModesRegistry.getLanguages());
ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m));
this._register(ModesRegistry.onDidAddLanguages((m) => this._registerLanguages(m)));
}
}
@@ -81,12 +86,14 @@ export class LanguagesRegistry {
});
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerOverrideIdentifiers(ModesRegistry.getLanguages().map(language => language.id));
this._onDidChange.fire();
}
private _registerLanguage(lang: ILanguageExtensionPoint): void {
const langId = lang.id;
let resolvedLanguage: IResolvedLanguage = null;
let resolvedLanguage: IResolvedLanguage;
if (hasOwnProperty.call(this._languages, langId)) {
resolvedLanguage = this._languages[langId];
} else {
@@ -110,7 +117,7 @@ export class LanguagesRegistry {
private _mergeLanguage(resolvedLanguage: IResolvedLanguage, lang: ILanguageExtensionPoint): void {
const langId = lang.id;
let primaryMime: string = null;
let primaryMime: string | null = null;
if (Array.isArray(lang.mimetypes) && lang.mimetypes.length > 0) {
resolvedLanguage.mimetypes.push(...lang.mimetypes);
@@ -163,7 +170,7 @@ export class LanguagesRegistry {
resolvedLanguage.aliases.push(langId);
let langAliases: string[] = null;
let langAliases: (string | null)[] | null = null;
if (typeof lang.aliases !== 'undefined' && Array.isArray(lang.aliases)) {
if (lang.aliases.length === 0) {
// signal that this language should not get a name
@@ -175,18 +182,19 @@ export class LanguagesRegistry {
if (langAliases !== null) {
for (let i = 0; i < langAliases.length; i++) {
if (!langAliases[i] || langAliases[i].length === 0) {
const langAlias = langAliases[i];
if (!langAlias || langAlias.length === 0) {
continue;
}
resolvedLanguage.aliases.push(langAliases[i]);
resolvedLanguage.aliases.push(langAlias);
}
}
let containsAliases = (langAliases !== null && langAliases.length > 0);
if (containsAliases && langAliases[0] === null) {
if (containsAliases && langAliases![0] === null) {
// signal that this language should not get a name
} else {
let bestName = (containsAliases ? langAliases[0] : null) || langId;
let bestName = (containsAliases ? langAliases![0] : null) || langId;
if (containsAliases || !resolvedLanguage.name) {
resolvedLanguage.name = bestName;
}
@@ -214,14 +222,14 @@ export class LanguagesRegistry {
return Object.keys(this._nameMap);
}
public getLanguageName(modeId: string): string {
public getLanguageName(modeId: string): string | null {
if (!hasOwnProperty.call(this._languages, modeId)) {
return null;
}
return this._languages[modeId].name;
}
public getModeIdForLanguageNameLowercase(languageNameLower: string): string {
public getModeIdForLanguageNameLowercase(languageNameLower: string): string | null {
if (!hasOwnProperty.call(this._lowercaseNameMap, languageNameLower)) {
return null;
}
@@ -235,7 +243,7 @@ export class LanguagesRegistry {
return this._languages[modeId].configurationFiles || [];
}
public getMimeForMode(modeId: string): string {
public getMimeForMode(modeId: string): string | null {
if (!hasOwnProperty.call(this._languages, modeId)) {
return null;
}
@@ -264,7 +272,7 @@ export class LanguagesRegistry {
);
}
public getLanguageIdentifier(_modeId: string | LanguageId): LanguageIdentifier {
public getLanguageIdentifier(_modeId: string | LanguageId): LanguageIdentifier | null {
if (_modeId === NULL_MODE_ID || _modeId === LanguageId.Null) {
return NULL_LANGUAGE_IDENTIFIER;
}
@@ -295,11 +303,11 @@ export class LanguagesRegistry {
return [];
}
public getModeIdsFromFilenameOrFirstLine(filename: string, firstLine?: string): string[] {
if (!filename && !firstLine) {
public getModeIdsFromFilepathOrFirstLine(filepath: string, firstLine?: string): string[] {
if (!filepath && !firstLine) {
return [];
}
let mimeTypes = mime.guessMimeTypes(filename, firstLine);
let mimeTypes = mime.guessMimeTypes(filepath, firstLine);
return this.extractModeIds(mimeTypes.join(','));
}

View File

@@ -2,13 +2,12 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const IModeService = createDecorator<IModeService>('modeService');
@@ -23,6 +22,11 @@ export interface ILanguageExtensionPoint {
configuration?: URI;
}
export interface ILanguageSelection extends IDisposable {
readonly languageIdentifier: LanguageIdentifier;
readonly onDidChange: Event<LanguageIdentifier>;
}
export interface IModeService {
_serviceBrand: any;
@@ -34,17 +38,18 @@ export interface IModeService {
getRegisteredLanguageNames(): string[];
getExtensions(alias: string): string[];
getFilenames(alias: string): string[];
getMimeForMode(modeId: string): string;
getLanguageName(modeId: string): string;
getModeIdForLanguageName(alias: string): string;
getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string;
getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string;
getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier;
getMimeForMode(modeId: string): string | null;
getLanguageName(modeId: string): string | null;
getModeIdForLanguageName(alias: string): string | null;
getModeIdByFilepathOrFirstLine(filepath: string, firstLine?: string): string | null;
getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string | null;
getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null;
getConfigurationFiles(modeId: string): URI[];
// --- instantiation
getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode;
getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise<IMode>;
getOrCreateModeByLanguageName(languageName: string): TPromise<IMode>;
getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): TPromise<IMode>;
create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection;
createByLanguageName(languageName: string): ILanguageSelection;
createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection;
triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void;
}

View File

@@ -2,16 +2,42 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { IMode, LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { FrankensteinMode } from 'vs/editor/common/modes/abstractMode';
import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
import { LanguagesRegistry } from 'vs/editor/common/services/languagesRegistry';
import { IModeService } from 'vs/editor/common/services/modeService';
import URI from 'vs/base/common/uri';
import { ILanguageSelection, IModeService } from 'vs/editor/common/services/modeService';
class LanguageSelection extends Disposable implements ILanguageSelection {
public languageIdentifier: LanguageIdentifier;
private readonly _selector: () => LanguageIdentifier;
private readonly _onDidChange: Emitter<LanguageIdentifier> = this._register(new Emitter<LanguageIdentifier>());
public readonly onDidChange: Event<LanguageIdentifier> = this._onDidChange.event;
constructor(onLanguagesMaybeChanged: Event<void>, selector: () => LanguageIdentifier) {
super();
this._selector = selector;
this.languageIdentifier = this._selector();
this._register(onLanguagesMaybeChanged(() => this._evaluate()));
}
private _evaluate(): void {
let languageIdentifier = this._selector();
if (languageIdentifier.id === this.languageIdentifier.id) {
// no change
return;
}
this.languageIdentifier = languageIdentifier;
this._onDidChange.fire(this.languageIdentifier);
}
}
export class ModeServiceImpl implements IModeService {
public _serviceBrand: any;
@@ -22,14 +48,18 @@ export class ModeServiceImpl implements IModeService {
private readonly _onDidCreateMode: Emitter<IMode> = new Emitter<IMode>();
public readonly onDidCreateMode: Event<IMode> = this._onDidCreateMode.event;
protected readonly _onLanguagesMaybeChanged: Emitter<void> = new Emitter<void>();
private readonly onLanguagesMaybeChanged: Event<void> = this._onLanguagesMaybeChanged.event;
constructor(warnOnOverwrite = false) {
this._instantiatedModes = {};
this._registry = new LanguagesRegistry(true, warnOnOverwrite);
this._registry.onDidChange(() => this._onLanguagesMaybeChanged.fire());
}
protected _onReady(): TPromise<boolean> {
return TPromise.as(true);
protected _onReady(): Promise<boolean> {
return Promise.resolve(true);
}
public isRegisteredMode(mimetypeOrModeId: string): boolean {
@@ -52,20 +82,20 @@ export class ModeServiceImpl implements IModeService {
return this._registry.getFilenames(alias);
}
public getMimeForMode(modeId: string): string {
public getMimeForMode(modeId: string): string | null {
return this._registry.getMimeForMode(modeId);
}
public getLanguageName(modeId: string): string {
public getLanguageName(modeId: string): string | null {
return this._registry.getLanguageName(modeId);
}
public getModeIdForLanguageName(alias: string): string {
public getModeIdForLanguageName(alias: string): string | null {
return this._registry.getModeIdForLanguageNameLowercase(alias);
}
public getModeIdByFilenameOrFirstLine(filename: string, firstLine?: string): string {
const modeIds = this._registry.getModeIdsFromFilenameOrFirstLine(filename, firstLine);
public getModeIdByFilepathOrFirstLine(filepath: string, firstLine?: string): string | null {
const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(filepath, firstLine);
if (modeIds.length > 0) {
return modeIds[0];
@@ -74,7 +104,7 @@ export class ModeServiceImpl implements IModeService {
return null;
}
public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string {
public getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string | null {
const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds);
if (modeIds.length > 0) {
@@ -84,7 +114,7 @@ export class ModeServiceImpl implements IModeService {
return null;
}
public getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier {
public getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null {
return this._registry.getLanguageIdentifier(modeId);
}
@@ -94,45 +124,45 @@ export class ModeServiceImpl implements IModeService {
// --- instantiation
public getMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): IMode {
const modeIds = this._registry.extractModeIds(commaSeparatedMimetypesOrCommaSeparatedIds);
let isPlainText = false;
for (let i = 0; i < modeIds.length; i++) {
if (this._instantiatedModes.hasOwnProperty(modeIds[i])) {
return this._instantiatedModes[modeIds[i]];
}
isPlainText = isPlainText || (modeIds[i] === 'plaintext');
}
if (isPlainText) {
// Try to do it synchronously
let r: IMode = null;
this.getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds).then((mode) => {
r = mode;
}).done(null, onUnexpectedError);
return r;
}
return null;
}
public getOrCreateMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): TPromise<IMode> {
return this._onReady().then(() => {
public create(commaSeparatedMimetypesOrCommaSeparatedIds: string): ILanguageSelection {
return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
// Fall back to plain text if no mode was found
return this._getOrCreateMode(modeId || 'plaintext');
return this._createModeAndGetLanguageIdentifier(modeId);
});
}
public getOrCreateModeByLanguageName(languageName: string): TPromise<IMode> {
return this._onReady().then(() => {
public createByLanguageName(languageName: string): ILanguageSelection {
return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
const modeId = this._getModeIdByLanguageName(languageName);
// Fall back to plain text if no mode was found
return this._getOrCreateMode(modeId || 'plaintext');
return this._createModeAndGetLanguageIdentifier(modeId);
});
}
private _getModeIdByLanguageName(languageName: string): string {
public createByFilepathOrFirstLine(filepath: string, firstLine?: string): ILanguageSelection {
return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine);
return this._createModeAndGetLanguageIdentifier(modeId);
});
}
private _createModeAndGetLanguageIdentifier(modeId: string | null): LanguageIdentifier {
// Fall back to plain text if no mode was found
const languageIdentifier = this.getLanguageIdentifier(modeId || 'plaintext') || NULL_LANGUAGE_IDENTIFIER;
this._getOrCreateMode(languageIdentifier.language);
return languageIdentifier;
}
public triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void {
const modeId = this.getModeId(commaSeparatedMimetypesOrCommaSeparatedIds);
// Fall back to plain text if no mode was found
this._getOrCreateMode(modeId || 'plaintext');
}
public waitForLanguageRegistration(): Promise<void> {
return this._onReady().then(() => { });
}
private _getModeIdByLanguageName(languageName: string): string | null {
const modeIds = this._registry.getModeIdsFromLanguageName(languageName);
if (modeIds.length > 0) {
@@ -142,17 +172,9 @@ export class ModeServiceImpl implements IModeService {
return null;
}
public getOrCreateModeByFilenameOrFirstLine(filename: string, firstLine?: string): TPromise<IMode> {
return this._onReady().then(() => {
const modeId = this.getModeIdByFilenameOrFirstLine(filename, firstLine);
// Fall back to plain text if no mode was found
return this._getOrCreateMode(modeId || 'plaintext');
});
}
private _getOrCreateMode(modeId: string): IMode {
if (!this._instantiatedModes.hasOwnProperty(modeId)) {
let languageIdentifier = this.getLanguageIdentifier(modeId);
let languageIdentifier = this.getLanguageIdentifier(modeId) || NULL_LANGUAGE_IDENTIFIER;
this._instantiatedModes[modeId] = new FrankensteinMode(languageIdentifier);
this._onDidCreateMode.fire(this._instantiatedModes[modeId]);

View File

@@ -2,25 +2,23 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { URI } from 'vs/base/common/uri';
import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel, ITextModelCreationOptions, ITextBufferFactory } from 'vs/editor/common/model';
import { IMode } from 'vs/editor/common/modes';
export const IModelService = createDecorator<IModelService>('modelService');
export interface IModelService {
_serviceBrand: any;
createModel(value: string | ITextBufferFactory, modeOrPromise: TPromise<IMode> | IMode, resource: URI, isForSimpleWidget?: boolean): ITextModel;
createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget?: boolean): ITextModel;
updateModel(model: ITextModel, value: string | ITextBufferFactory): void;
setMode(model: ITextModel, modeOrPromise: TPromise<IMode> | IMode): void;
setMode(model: ITextModel, languageSelection: ILanguageSelection): void;
destroyModel(resource: URI): void;
@@ -28,7 +26,7 @@ export interface IModelService {
getCreationOptions(language: string, resource: URI, isForSimpleWidget: boolean): ITextModelCreationOptions;
getModel(resource: URI): ITextModel;
getModel(resource: URI): ITextModel | null;
onModelAdded: Event<ITextModel>;

View File

@@ -2,39 +2,42 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import * as nls from 'vs/nls';
import * as network from 'vs/base/common/network';
import { Event, Emitter } from 'vs/base/common/event';
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { Emitter, Event } from 'vs/base/common/event';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { Range } from 'vs/editor/common/core/range';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
import { IModelService } from 'vs/editor/common/services/modelService';
import * as platform from 'vs/base/common/platform';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { themeColorFromId, ThemeColor } from 'vs/platform/theme/common/themeService';
import { overviewRulerWarning, overviewRulerError, overviewRulerInfo } from 'vs/editor/common/view/editorColorRegistry';
import { ITextModel, IModelDeltaDecoration, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, DefaultEndOfLine, ITextModelCreationOptions, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBufferFactory, ITextBuffer, EndOfLinePreference } from 'vs/editor/common/model';
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
import { escape } from 'vs/base/common/strings';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import * as network from 'vs/base/common/network';
import { basename } from 'vs/base/common/paths';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { EditOperation } from 'vs/editor/common/core/editOperation';
import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model';
import { ClassName } from 'vs/editor/common/model/intervalTree';
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
import { IModelLanguageChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { overviewRulerError, overviewRulerInfo, overviewRulerWarning } from 'vs/editor/common/view/editorColorRegistry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IMarker, IMarkerService, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers';
import { ThemeColor, themeColorFromId } from 'vs/platform/theme/common/themeService';
function MODEL_ID(resource: URI): string {
return resource.toString();
}
class ModelData implements IDisposable {
model: ITextModel;
public readonly model: ITextModel;
private _languageSelection: ILanguageSelection | null;
private _languageSelectionListener: IDisposable | null;
private _markerDecorations: string[];
private _modelEventListeners: IDisposable[];
@@ -46,6 +49,9 @@ class ModelData implements IDisposable {
) {
this.model = model;
this._languageSelection = null;
this._languageSelectionListener = null;
this._markerDecorations = [];
this._modelEventListeners = [];
@@ -53,15 +59,33 @@ class ModelData implements IDisposable {
this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
}
private _disposeLanguageSelection(): void {
if (this._languageSelectionListener) {
this._languageSelectionListener.dispose();
this._languageSelectionListener = null;
}
if (this._languageSelection) {
this._languageSelection.dispose();
this._languageSelection = null;
}
}
public dispose(): void {
this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, []);
this._modelEventListeners = dispose(this._modelEventListeners);
this.model = null;
this._disposeLanguageSelection();
}
public acceptMarkerDecorations(newDecorations: IModelDeltaDecoration[]): void {
this._markerDecorations = this.model.deltaDecorations(this._markerDecorations, newDecorations);
}
public setLanguage(languageSelection: ILanguageSelection): void {
this._disposeLanguageSelection();
this._languageSelection = languageSelection;
this._languageSelectionListener = this._languageSelection.onDidChange(() => this.model.setMode(languageSelection.languageIdentifier));
this.model.setMode(languageSelection.languageIdentifier);
}
}
class ModelMarkerHandler {
@@ -73,8 +97,8 @@ class ModelMarkerHandler {
let newModelDecorations: IModelDeltaDecoration[] = markers.map((marker) => {
return {
range: this._createDecorationRange(modelData.model, marker),
options: this._createDecorationOption(marker)
range: ModelMarkerHandler._createDecorationRange(modelData.model, marker),
options: ModelMarkerHandler._createDecorationOption(marker)
};
});
@@ -85,9 +109,12 @@ class ModelMarkerHandler {
let ret = Range.lift(rawMarker);
if (rawMarker.severity === MarkerSeverity.Hint && Range.spansMultipleLines(ret)) {
// never render hints on multiple lines
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn);
if (rawMarker.severity === MarkerSeverity.Hint) {
if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) {
// * never render hints on multiple lines
// * make enough space for three dots
ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2);
}
}
ret = model.validateRange(ret);
@@ -124,10 +151,9 @@ class ModelMarkerHandler {
private static _createDecorationOption(marker: IMarker): IModelDecorationOptions {
let className: string;
let color: ThemeColor;
let darkColor: ThemeColor;
let color: ThemeColor | undefined = undefined;
let zIndex: number;
let inlineClassName: string;
let inlineClassName: string | undefined = undefined;
switch (marker.severity) {
case MarkerSeverity.Hint:
@@ -141,20 +167,17 @@ class ModelMarkerHandler {
case MarkerSeverity.Warning:
className = ClassName.EditorWarningDecoration;
color = themeColorFromId(overviewRulerWarning);
darkColor = themeColorFromId(overviewRulerWarning);
zIndex = 20;
break;
case MarkerSeverity.Info:
className = ClassName.EditorInfoDecoration;
color = themeColorFromId(overviewRulerInfo);
darkColor = themeColorFromId(overviewRulerInfo);
zIndex = 10;
break;
case MarkerSeverity.Error:
default:
className = ClassName.EditorErrorDecoration;
color = themeColorFromId(overviewRulerError);
darkColor = themeColorFromId(overviewRulerError);
zIndex = 30;
break;
}
@@ -165,32 +188,37 @@ class ModelMarkerHandler {
}
}
let hoverMessage: MarkdownString = null;
let { message, source, relatedInformation } = marker;
let hoverMessage: MarkdownString | null = null;
let { message, source, relatedInformation, code } = marker;
if (typeof message === 'string') {
message = message.trim();
hoverMessage = new MarkdownString();
// Disable markdown renderer sanitize to allow html
// Hence, escape all input strings
hoverMessage.sanitize = false;
hoverMessage.appendMarkdown(`<div>`);
hoverMessage.appendMarkdown(`<span style='font-family: Monaco, Menlo, Consolas, "Droid Sans Mono", "Inconsolata", "Courier New", monospace, "Droid Sans Fallback"; white-space: pre-wrap;'>${escape(message.trim())}</span>`);
if (source) {
if (/\n/g.test(message)) {
message = nls.localize('diagAndSourceMultiline', "[{0}]\n{1}", source, message);
} else {
message = nls.localize('diagAndSource', "[{0}] {1}", source, message);
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:6px;'>${escape(source)}</span>`);
if (code) {
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:2px;'>(${escape(code)})</span>`);
}
} else if (code) {
hoverMessage.appendMarkdown(`<span style='opacity: 0.6; padding-left:6px;'>(${escape(code)})</span>`);
}
hoverMessage.appendMarkdown(`</div>`);
hoverMessage = new MarkdownString().appendCodeblock('_', message);
if (!isFalsyOrEmpty(relatedInformation)) {
hoverMessage.appendMarkdown('\n');
if (isNonEmptyArray(relatedInformation)) {
hoverMessage.appendMarkdown(`<ul>`);
for (const { message, resource, startLineNumber, startColumn } of relatedInformation) {
hoverMessage.appendMarkdown(
`* [${basename(resource.path)}(${startLineNumber}, ${startColumn})](${resource.toString(false)}#${startLineNumber},${startColumn}): `
);
hoverMessage.appendText(`${message}`);
hoverMessage.appendMarkdown('\n');
hoverMessage.appendMarkdown(`<li>`);
hoverMessage.appendMarkdown(`<a href='#' data-href='${resource.toString(false)}#${startLineNumber},${startColumn}'>${escape(basename(resource.path))}(${startLineNumber}, ${startColumn})</a>`);
hoverMessage.appendMarkdown(`<span>: ${escape(message)}</span>`);
hoverMessage.appendMarkdown(`</li>`);
}
hoverMessage.appendMarkdown('\n');
hoverMessage.appendMarkdown(`</ul>`);
}
}
@@ -201,7 +229,6 @@ class ModelMarkerHandler {
showIfCollapsed: true,
overviewRuler: {
color,
darkColor,
position: OverviewRulerLane.Right
},
zIndex,
@@ -210,33 +237,39 @@ class ModelMarkerHandler {
}
}
interface IRawEditorConfig {
tabSize?: any;
insertSpaces?: any;
detectIndentation?: any;
trimAutoWhitespace?: any;
creationOptions?: any;
largeFileOptimizations?: any;
}
interface IRawConfig {
files?: {
eol?: any;
};
editor?: {
tabSize?: any;
insertSpaces?: any;
detectIndentation?: any;
trimAutoWhitespace?: any;
creationOptions?: any;
largeFileOptimizations?: any;
};
eol?: any;
editor?: IRawEditorConfig;
}
const DEFAULT_EOL = (platform.isLinux || platform.isMacintosh) ? DefaultEndOfLine.LF : DefaultEndOfLine.CRLF;
export class ModelServiceImpl implements IModelService {
export class ModelServiceImpl extends Disposable implements IModelService {
public _serviceBrand: any;
private _markerService: IMarkerService;
private _markerServiceSubscription: IDisposable;
private _configurationService: IConfigurationService;
private _configurationServiceSubscription: IDisposable;
private _resourcePropertiesService: ITextResourcePropertiesService;
private readonly _onModelAdded: Emitter<ITextModel>;
private readonly _onModelRemoved: Emitter<ITextModel>;
private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }>;
private readonly _onModelAdded: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
public readonly onModelAdded: Event<ITextModel> = this._onModelAdded.event;
private readonly _onModelRemoved: Emitter<ITextModel> = this._register(new Emitter<ITextModel>());
public readonly onModelRemoved: Event<ITextModel> = this._onModelRemoved.event;
private readonly _onModelModeChanged: Emitter<{ model: ITextModel; oldModeId: string; }> = this._register(new Emitter<{ model: ITextModel; oldModeId: string; }>({ leakWarningThreshold: 500 }));
public readonly onModelModeChanged: Event<{ model: ITextModel; oldModeId: string; }> = this._onModelModeChanged.event;
private _modelCreationOptionsByLanguageAndResource: {
[languageAndResource: string]: ITextModelCreationOptions;
@@ -250,14 +283,14 @@ export class ModelServiceImpl implements IModelService {
constructor(
@IMarkerService markerService: IMarkerService,
@IConfigurationService configurationService: IConfigurationService,
@ITextResourcePropertiesService resourcePropertiesService: ITextResourcePropertiesService,
) {
super();
this._markerService = markerService;
this._configurationService = configurationService;
this._resourcePropertiesService = resourcePropertiesService;
this._models = {};
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
this._onModelAdded = new Emitter<ITextModel>();
this._onModelRemoved = new Emitter<ITextModel>();
this._onModelModeChanged = new Emitter<{ model: ITextModel; oldModeId: string; }>();
if (this._markerService) {
this._markerServiceSubscription = this._markerService.onMarkerChanged(this._handleMarkerChange, this);
@@ -285,7 +318,7 @@ export class ModelServiceImpl implements IModelService {
}
let newDefaultEOL = DEFAULT_EOL;
const eol = config.files && config.files.eol;
const eol = config.eol;
if (eol === '\r\n') {
newDefaultEOL = DefaultEndOfLine.CRLF;
} else if (eol === '\n') {
@@ -321,7 +354,9 @@ export class ModelServiceImpl implements IModelService {
public getCreationOptions(language: string, resource: URI, isForSimpleWidget: boolean): ITextModelCreationOptions {
let creationOptions = this._modelCreationOptionsByLanguageAndResource[language + resource];
if (!creationOptions) {
creationOptions = ModelServiceImpl._readModelOptions(this._configurationService.getValue({ overrideIdentifier: language, resource }), isForSimpleWidget);
const editor = this._configurationService.getValue<IRawEditorConfig>('editor', { overrideIdentifier: language, resource });
const eol = this._resourcePropertiesService.getEOL(resource, language);
creationOptions = ModelServiceImpl._readModelOptions({ editor, eol }, isForSimpleWidget);
this._modelCreationOptionsByLanguageAndResource[language + resource] = creationOptions;
}
return creationOptions;
@@ -374,6 +409,7 @@ export class ModelServiceImpl implements IModelService {
this._markerServiceSubscription.dispose();
}
this._configurationServiceSubscription.dispose();
super.dispose();
}
private _handleMarkerChange(changedResources: URI[]): void {
@@ -494,14 +530,14 @@ export class ModelServiceImpl implements IModelService {
return [EditOperation.replaceMove(oldRange, textBuffer.getValueInRange(newRange, EndOfLinePreference.TextDefined))];
}
public createModel(value: string | ITextBufferFactory, modeOrPromise: TPromise<IMode> | IMode, resource: URI, isForSimpleWidget: boolean = false): ITextModel {
public createModel(value: string | ITextBufferFactory, languageSelection: ILanguageSelection | null, resource: URI, isForSimpleWidget: boolean = false): ITextModel {
let modelData: ModelData;
if (!modeOrPromise || TPromise.is(modeOrPromise)) {
modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget);
this.setMode(modelData.model, modeOrPromise);
if (languageSelection) {
modelData = this._createModelData(value, languageSelection.languageIdentifier, resource, isForSimpleWidget);
this.setMode(modelData.model, languageSelection);
} else {
modelData = this._createModelData(value, modeOrPromise.getLanguageIdentifier(), resource, isForSimpleWidget);
modelData = this._createModelData(value, PLAINTEXT_LANGUAGE_IDENTIFIER, resource, isForSimpleWidget);
}
// handle markers (marker service => model)
@@ -514,19 +550,15 @@ export class ModelServiceImpl implements IModelService {
return modelData.model;
}
public setMode(model: ITextModel, modeOrPromise: TPromise<IMode> | IMode): void {
if (!modeOrPromise) {
public setMode(model: ITextModel, languageSelection: ILanguageSelection): void {
if (!languageSelection) {
return;
}
if (TPromise.is(modeOrPromise)) {
modeOrPromise.then((mode) => {
if (!model.isDisposed()) {
model.setMode(mode.getLanguageIdentifier());
}
});
} else {
model.setMode(modeOrPromise.getLanguageIdentifier());
let modelData = this._models[MODEL_ID(model.uri)];
if (!modelData) {
return;
}
modelData.setLanguage(languageSelection);
}
public destroyModel(resource: URI): void {
@@ -550,7 +582,7 @@ export class ModelServiceImpl implements IModelService {
return ret;
}
public getModel(resource: URI): ITextModel {
public getModel(resource: URI): ITextModel | null {
let modelId = MODEL_ID(resource);
let modelData = this._models[modelId];
if (!modelData) {
@@ -559,18 +591,6 @@ export class ModelServiceImpl implements IModelService {
return modelData.model;
}
public get onModelAdded(): Event<ITextModel> {
return this._onModelAdded ? this._onModelAdded.event : null;
}
public get onModelRemoved(): Event<ITextModel> {
return this._onModelRemoved ? this._onModelRemoved.event : null;
}
public get onModelModeChanged(): Event<{ model: ITextModel; oldModeId: string; }> {
return this._onModelModeChanged ? this._onModelModeChanged.event : null;
}
// --- end IModelService
private _onWillDispose(model: ITextModel): void {

View File

@@ -2,14 +2,13 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { IDisposable, IReference } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model';
import { IEditorModel } from 'vs/platform/editor/common/editor';
import { IDisposable, IReference } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITextModelService = createDecorator<ITextModelService>('textModelService');
@@ -33,7 +32,7 @@ export interface ITextModelContentProvider {
/**
* Given a resource, return the content of the resource as `ITextModel`.
*/
provideTextContent(resource: URI): TPromise<ITextModel>;
provideTextContent(resource: URI): Thenable<ITextModel>;
}
export interface ITextEditorModel extends IEditorModel {
@@ -42,4 +41,6 @@ export interface ITextEditorModel extends IEditorModel {
* Provides access to the underlying `ITextModel`.
*/
textEditorModel: ITextModel;
isReadonly(): boolean;
}

View File

@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import URI from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { IPosition } from 'vs/editor/common/core/position';
import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export const ITextResourceConfigurationService = createDecorator<ITextResourceConfigurationService>('textResourceConfigurationService');
@@ -32,4 +32,16 @@ export interface ITextResourceConfigurationService {
getValue<T>(resource: URI, section?: string): T;
getValue<T>(resource: URI, position?: IPosition, section?: string): T;
}
export const ITextResourcePropertiesService = createDecorator<ITextResourcePropertiesService>('textResourcePropertiesService');
export interface ITextResourcePropertiesService {
_serviceBrand: any;
/**
* Returns the End of Line characters for the given resource
*/
getEOL(resource: URI, language?: string): string;
}

View File

@@ -3,15 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import URI from 'vs/base/common/uri';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { URI } from 'vs/base/common/uri';
import { IPosition, Position } from 'vs/editor/common/core/position';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService';
import { basename } from 'vs/base/common/paths';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration';
export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService {
@@ -32,17 +31,26 @@ export class TextResourceConfigurationService extends Disposable implements ITex
getValue<T>(resource: URI, section?: string): T;
getValue<T>(resource: URI, at?: IPosition, section?: string): T;
getValue<T>(resource: URI, arg2?: any, arg3?: any): T {
const position: IPosition = Position.isIPosition(arg2) ? arg2 : null;
const section: string = position ? (typeof arg3 === 'string' ? arg3 : void 0) : (typeof arg2 === 'string' ? arg2 : void 0);
if (typeof arg3 === 'string') {
return this._getValue(resource, Position.isIPosition(arg2) ? arg2 : null, arg3);
}
return this._getValue(resource, null, typeof arg2 === 'string' ? arg2 : undefined);
}
private _getValue<T>(resource: URI, position: IPosition | null, section: string | undefined): T {
const language = resource ? this.getLanguage(resource, position) : void 0;
if (typeof section === 'undefined') {
return this.configurationService.getValue<T>({ resource, overrideIdentifier: language });
}
return this.configurationService.getValue<T>(section, { resource, overrideIdentifier: language });
}
private getLanguage(resource: URI, position: IPosition): string {
private getLanguage(resource: URI, position: IPosition | null): string | null {
const model = this.modelService.getModel(resource);
if (model) {
return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column)).language : model.getLanguageIdentifier().language;
return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language;
}
return this.modeService.getModeIdByFilenameOrFirstLine(basename(resource.path));
return this.modeService.getModeIdByFilepathOrFirstLine(resource.path);
}
}

View File

@@ -2,13 +2,10 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { ShallowCancelThenPromise } from 'vs/base/common/async';
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { IModelService } from 'vs/editor/common/services/modelService';
import { URI } from 'vs/base/common/uri';
import { EditorWorkerClient } from 'vs/editor/common/services/editorWorkerServiceImpl';
import { IModelService } from 'vs/editor/common/services/modelService';
/**
* Create a new web worker that has model syncing capabilities built in.
@@ -29,12 +26,12 @@ export interface MonacoWebWorker<T> {
/**
* Get a proxy to the arbitrary loaded code.
*/
getProxy(): TPromise<T>;
getProxy(): Promise<T>;
/**
* Synchronize (send) the models at `resources` to the web worker,
* making them available in the monaco.worker.getMirrorModels().
*/
withSyncedResources(resources: URI[]): TPromise<T>;
withSyncedResources(resources: URI[]): Promise<T>;
}
export interface IWebWorkerOptions {
@@ -56,8 +53,8 @@ export interface IWebWorkerOptions {
class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWorker<T> {
private _foreignModuleId: string;
private _foreignModuleCreateData: any;
private _foreignProxy: TPromise<T>;
private _foreignModuleCreateData: any | null;
private _foreignProxy: Promise<T> | null;
constructor(modelService: IModelService, opts: IWebWorkerOptions) {
super(modelService, opts.label);
@@ -66,18 +63,17 @@ class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWork
this._foreignProxy = null;
}
private _getForeignProxy(): TPromise<T> {
private _getForeignProxy(): Promise<T> {
if (!this._foreignProxy) {
this._foreignProxy = new ShallowCancelThenPromise(this._getProxy().then((proxy) => {
this._foreignProxy = this._getProxy().then((proxy) => {
return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData).then((foreignMethods) => {
this._foreignModuleId = null;
this._foreignModuleCreateData = null;
let proxyMethodRequest = (method: string, args: any[]): TPromise<any> => {
let proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
return proxy.fmr(method, args);
};
let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => TPromise<any>): Function => {
let createProxyMethod = (method: string, proxyMethodRequest: (method: string, args: any[]) => Promise<any>): Function => {
return function () {
let args = Array.prototype.slice.call(arguments, 0);
return proxyMethodRequest(method, args);
@@ -91,16 +87,16 @@ class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWork
return foreignProxy;
});
}));
});
}
return this._foreignProxy;
}
public getProxy(): TPromise<T> {
public getProxy(): Promise<T> {
return this._getForeignProxy();
}
public withSyncedResources(resources: URI[]): TPromise<T> {
public withSyncedResources(resources: URI[]): Promise<T> {
return this._withSyncedResources(resources).then(_ => this.getProxy());
}
}