mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from master
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
73
src/vs/editor/common/services/getIconClasses.ts
Normal file
73
src/vs/editor/common/services/getIconClasses.ts
Normal 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
|
||||
}
|
||||
@@ -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(','));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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>;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user