Skip to content

Commit 54aeb11

Browse files
committed
Let model builder return ISourceText.
Callers use ISourceText instead of IRawText or IRawTextProvider. Model service holds the ownership of IRawText, Creation options and resolved options for model
1 parent 38e8328 commit 54aeb11

13 files changed

Lines changed: 107 additions & 184 deletions

File tree

src/vs/editor/common/services/modelService.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,17 @@ import Event from 'vs/base/common/event';
88
import URI from 'vs/base/common/uri';
99
import { TPromise } from 'vs/base/common/winjs.base';
1010
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
11-
import { IModel, IRawText, ITextModelCreationOptions } from 'vs/editor/common/editorCommon';
11+
import { IModel, ITextSource, ITextModelCreationOptions } from 'vs/editor/common/editorCommon';
1212
import { IMode } from 'vs/editor/common/modes';
1313

1414
export var IModelService = createDecorator<IModelService>('modelService');
1515

16-
export interface IRawTextProvider {
17-
getFirstLine(): string;
18-
getEntireContent(): string;
19-
toRawText(opts: ITextModelCreationOptions): IRawText;
20-
}
21-
2216
export interface IModelService {
2317
_serviceBrand: any;
2418

25-
createModel(value: string | IRawTextProvider, modeOrPromise: TPromise<IMode> | IMode, resource: URI): IModel;
19+
createModel(value: string | ITextSource, modeOrPromise: TPromise<IMode> | IMode, resource: URI): IModel;
2620

27-
updateModel(model: IModel, value: string | IRawTextProvider): void;
21+
updateModel(model: IModel, value: string | ITextSource): void;
2822

2923
setMode(model: IModel, modeOrPromise: TPromise<IMode> | IMode): void;
3024

src/vs/editor/common/services/modelServiceImpl.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@ import { Range } from 'vs/editor/common/core/range';
1818
import * as editorCommon from 'vs/editor/common/editorCommon';
1919
import { Model } from 'vs/editor/common/model/model';
2020
import { IMode, LanguageIdentifier } from 'vs/editor/common/modes';
21-
import { IModelService, IRawTextProvider } from 'vs/editor/common/services/modelService';
21+
import { IModelService } from 'vs/editor/common/services/modelService';
2222
import * as platform from 'vs/base/common/platform';
2323
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
2424
import { DEFAULT_INDENTATION, DEFAULT_TRIM_AUTO_WHITESPACE } from 'vs/editor/common/config/defaultConfig';
2525
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
2626
import { RawText } from 'vs/editor/common/model/textModel';
27+
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
2728

2829
function MODEL_ID(resource: URI): string {
2930
return resource.toString();
@@ -338,15 +339,16 @@ export class ModelServiceImpl implements IModelService {
338339

339340
// --- begin IModelService
340341

341-
private _createModelData(value: string | IRawTextProvider, languageIdentifier: LanguageIdentifier, resource: URI): ModelData {
342+
private _createModelData(value: string | editorCommon.ITextSource, languageIdentifier: LanguageIdentifier, resource: URI): ModelData {
342343
// create & save the model
343344
const options = this.getCreationOptions(languageIdentifier.language);
344345

345346
let model: Model;
346347
if (typeof value === 'string') {
347348
model = Model.createFromString(value, options, languageIdentifier, resource);
348349
} else {
349-
model = new Model(value.toRawText(options), languageIdentifier, resource);
350+
const rawText = ModelServiceImpl.toRawText(value, options);
351+
model = new Model(rawText, languageIdentifier, resource);
350352
}
351353
let modelId = MODEL_ID(model.uri);
352354

@@ -361,13 +363,50 @@ export class ModelServiceImpl implements IModelService {
361363
return modelData;
362364
}
363365

364-
public updateModel(model: editorCommon.IModel, value: string | IRawTextProvider): void {
366+
private static toRawText(textSource: editorCommon.ITextSource, opts: editorCommon.ITextModelCreationOptions): editorCommon.IRawText {
367+
let lineFeedCnt = textSource.lines.length - 1;
368+
let EOL = textSource.EOL;
369+
if (lineFeedCnt === 0) {
370+
// This is an empty file or a file with precisely one line
371+
EOL = (opts.defaultEOL === editorCommon.DefaultEndOfLine.LF ? '\n' : '\r\n');
372+
}
373+
374+
let resolvedOpts: editorCommon.TextModelResolvedOptions;
375+
if (opts.detectIndentation) {
376+
let guessedIndentation = guessIndentation(textSource.lines, opts.tabSize, opts.insertSpaces);
377+
resolvedOpts = new editorCommon.TextModelResolvedOptions({
378+
tabSize: guessedIndentation.tabSize,
379+
insertSpaces: guessedIndentation.insertSpaces,
380+
trimAutoWhitespace: opts.trimAutoWhitespace,
381+
defaultEOL: opts.defaultEOL
382+
});
383+
} else {
384+
resolvedOpts = new editorCommon.TextModelResolvedOptions({
385+
tabSize: opts.tabSize,
386+
insertSpaces: opts.insertSpaces,
387+
trimAutoWhitespace: opts.trimAutoWhitespace,
388+
defaultEOL: opts.defaultEOL
389+
});
390+
}
391+
392+
return {
393+
BOM: textSource.BOM,
394+
EOL: EOL,
395+
lines: textSource.lines,
396+
length: textSource.length,
397+
containsRTL: textSource.containsRTL,
398+
isBasicASCII: textSource.isBasicASCII,
399+
options: resolvedOpts
400+
};
401+
}
402+
403+
public updateModel(model: editorCommon.IModel, value: string | editorCommon.ITextSource): void {
365404
let rawText: editorCommon.IRawText;
366405
if (typeof value === 'string') {
367406
rawText = RawText.fromStringWithModelOptions(value, model);
368407
} else {
369408
let creationOptions = this.getCreationOptions(model.getLanguageIdentifier().language);
370-
rawText = value.toRawText(creationOptions);
409+
rawText = ModelServiceImpl.toRawText(value, creationOptions);
371410
}
372411

373412
// Return early if the text is already set in that form
@@ -379,7 +418,7 @@ export class ModelServiceImpl implements IModelService {
379418
model.setValueFromRawText(rawText);
380419
}
381420

382-
public createModel(value: string | IRawTextProvider, modeOrPromise: TPromise<IMode> | IMode, resource: URI): editorCommon.IModel {
421+
public createModel(value: string | editorCommon.ITextSource, modeOrPromise: TPromise<IMode> | IMode, resource: URI): editorCommon.IModel {
383422
let modelData: ModelData;
384423

385424
if (!modeOrPromise || TPromise.is(modeOrPromise)) {

src/vs/editor/node/model/modelBuilder.ts

Lines changed: 18 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66

77
import { IStringStream } from 'vs/platform/files/common/files';
88
import * as crypto from 'crypto';
9-
import { DefaultEndOfLine, ITextModelCreationOptions, TextModelResolvedOptions, IRawText } from 'vs/editor/common/editorCommon';
9+
import { ITextSource } from 'vs/editor/common/editorCommon';
1010
import * as strings from 'vs/base/common/strings';
11-
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
1211
import { TPromise } from 'vs/base/common/winjs.base';
1312
import { CharCode } from 'vs/base/common/charCode';
14-
import { IRawTextProvider } from 'vs/editor/common/services/modelService';
13+
14+
export interface ModelBuilderResult {
15+
readonly hash: string;
16+
readonly value: ITextSource;
17+
}
1518

1619
class ModelLineBasedBuilder {
1720

@@ -42,119 +45,31 @@ class ModelLineBasedBuilder {
4245
this.hash.update(lines.join('\n') + '\n');
4346
}
4447

45-
public finish(totalLength: number, carriageReturnCnt: number, containsRTL: boolean, isBasicASCII: boolean): ModelBuilderResult {
46-
return new ModelBuilderResult(this.BOM, this.lines, totalLength, carriageReturnCnt, containsRTL, isBasicASCII, this.hash.digest('hex'));
47-
}
48-
}
49-
50-
export class ModelBuilderResult implements IRawTextProvider {
51-
/**
52-
* The BOM (leading character sequence of the file).
53-
*/
54-
private readonly BOM: string;
55-
/**
56-
* The text split into lines.
57-
*/
58-
private readonly lines: string[];
59-
/**
60-
* The entire text length.
61-
*/
62-
private readonly length: number;
63-
/**
64-
* Number of lines with EOL \r\n
65-
*/
66-
private readonly carriageReturnCnt: number;
67-
/**
68-
* The text contains Unicode characters classified as "R" or "AL".
69-
*/
70-
private readonly containsRTL: boolean;
71-
/**
72-
* The text contains only characters inside the ASCII range 32-126 or \t \r \n
73-
*/
74-
private readonly isBasicASCII: boolean;
75-
/**
76-
* The content hash.
77-
*/
78-
public readonly hash: string;
79-
80-
constructor(BOM: string, lines: string[], length: number, carriageReturnCnt: number, containsRTL: boolean, isBasicASCII: boolean, hash: string) {
81-
this.BOM = BOM;
82-
this.lines = lines;
83-
this.length = length;
84-
this.carriageReturnCnt = carriageReturnCnt;
85-
this.containsRTL = containsRTL;
86-
this.isBasicASCII = isBasicASCII;
87-
this.hash = hash;
88-
}
89-
90-
public getEntireContent(): string {
91-
let lineFeedCnt = this.lines.length - 1;
92-
if (lineFeedCnt === 0) {
93-
// Just one line, EOL does not matter
94-
return this.lines[0];
95-
}
96-
97-
let EOL = '';
98-
if (this.carriageReturnCnt > lineFeedCnt / 2) {
99-
// More than half of the file contains \r\n ending lines
100-
EOL = '\r\n';
101-
} else {
102-
// At least one line more ends in \n
103-
EOL = '\n';
104-
}
105-
return this.lines.join(EOL);
106-
}
107-
108-
public getFirstLine(): string {
109-
return this.lines[0];
110-
}
111-
112-
public toRawText(opts: ITextModelCreationOptions): IRawText {
113-
48+
public finish(length: number, carriageReturnCnt: number, containsRTL: boolean, isBasicASCII: boolean): ModelBuilderResult {
11449
let lineFeedCnt = this.lines.length - 1;
11550
let EOL = '';
116-
if (lineFeedCnt === 0) {
117-
// This is an empty file or a file with precisely one line
118-
EOL = (opts.defaultEOL === DefaultEndOfLine.LF ? '\n' : '\r\n');
119-
} else if (this.carriageReturnCnt > lineFeedCnt / 2) {
51+
if (carriageReturnCnt > lineFeedCnt / 2) {
12052
// More than half of the file contains \r\n ending lines
12153
EOL = '\r\n';
12254
} else {
12355
// At least one line more ends in \n
12456
EOL = '\n';
12557
}
126-
127-
let resolvedOpts: TextModelResolvedOptions;
128-
if (opts.detectIndentation) {
129-
let guessedIndentation = guessIndentation(this.lines, opts.tabSize, opts.insertSpaces);
130-
resolvedOpts = new TextModelResolvedOptions({
131-
tabSize: guessedIndentation.tabSize,
132-
insertSpaces: guessedIndentation.insertSpaces,
133-
trimAutoWhitespace: opts.trimAutoWhitespace,
134-
defaultEOL: opts.defaultEOL
135-
});
136-
} else {
137-
resolvedOpts = new TextModelResolvedOptions({
138-
tabSize: opts.tabSize,
139-
insertSpaces: opts.insertSpaces,
140-
trimAutoWhitespace: opts.trimAutoWhitespace,
141-
defaultEOL: opts.defaultEOL
142-
});
143-
}
144-
14558
return {
146-
BOM: this.BOM,
147-
EOL: EOL,
148-
lines: this.lines,
149-
length: this.length,
150-
containsRTL: this.containsRTL,
151-
isBasicASCII: this.isBasicASCII,
152-
options: resolvedOpts
59+
hash: this.hash.digest('hex'),
60+
value: {
61+
BOM: this.BOM,
62+
lines: this.lines,
63+
length,
64+
containsRTL: containsRTL,
65+
EOL,
66+
isBasicASCII,
67+
}
15368
};
15469
}
15570
}
15671

157-
export function computeHash(rawText: IRawText): string {
72+
export function computeHash(rawText: ITextSource): string {
15873
let hash = crypto.createHash('sha1');
15974
for (let i = 0, len = rawText.lines.length; i < len; i++) {
16075
hash.update(rawText.lines[i] + '\n');

src/vs/editor/test/node/model/modelBuilder.test.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,38 +6,45 @@
66

77
import * as assert from 'assert';
88
import { ModelBuilder, computeHash } from 'vs/editor/node/model/modelBuilder';
9-
import { ITextModelCreationOptions, IRawText } from 'vs/editor/common/editorCommon';
9+
import { ITextModelCreationOptions, ITextSource } from 'vs/editor/common/editorCommon';
1010
import { TextModel } from 'vs/editor/common/model/textModel';
1111
import * as strings from 'vs/base/common/strings';
1212

1313
export function testModelBuilder(chunks: string[], opts: ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS): string {
14-
let expectedRawText = TextModel.toRawText(chunks.join(''), opts);
15-
let expectedHash = computeHash(expectedRawText);
14+
let rawText = TextModel.toRawText(chunks.join(''), opts);
15+
let expectedTextSource: ITextSource = {
16+
BOM: rawText.BOM,
17+
containsRTL: rawText.containsRTL,
18+
EOL: rawText.EOL,
19+
isBasicASCII: rawText.isBasicASCII,
20+
length: rawText.length,
21+
lines: rawText.lines
22+
};
23+
let expectedHash = computeHash(expectedTextSource);
1624

1725
let builder = new ModelBuilder();
1826
for (let i = 0, len = chunks.length; i < len; i++) {
1927
builder.acceptChunk(chunks[i]);
2028
}
2129
let actual = builder.finish();
2230

23-
let actualRawText = actual.toRawText(opts);
31+
let actualTextSource = actual.value;
2432
let actualHash = actual.hash;
2533

2634
assert.equal(actualHash, expectedHash);
27-
assert.deepEqual(actualRawText, expectedRawText);
35+
assert.deepEqual(actualTextSource, expectedTextSource);
2836

2937
return expectedHash;
3038
}
3139

32-
function toRawText(lines: string[]): IRawText {
40+
function toRawText(lines: string[]): ITextSource {
3341
return {
3442
BOM: '',
3543
lines: lines,
3644
EOL: '\n',
3745
length: 0,
3846
containsRTL: false,
39-
isBasicASCII: true,
40-
options: null
47+
isBasicASCII: true
4148
};
4249
}
4350

src/vs/workbench/common/editor/textEditorModel.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55
'use strict';
66

77
import { TPromise } from 'vs/base/common/winjs.base';
8-
import { EndOfLinePreference, IModel } from 'vs/editor/common/editorCommon';
8+
import { EndOfLinePreference, IModel, ITextSource } from 'vs/editor/common/editorCommon';
99
import { IMode } from 'vs/editor/common/modes';
1010
import { EditorModel } from 'vs/workbench/common/editor';
1111
import URI from 'vs/base/common/uri';
1212
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
1313
import { IModeService } from 'vs/editor/common/services/modeService';
14-
import { IRawTextProvider, IModelService } from 'vs/editor/common/services/modelService';
14+
import { IModelService } from 'vs/editor/common/services/modelService';
1515
import { IDisposable } from 'vs/base/common/lifecycle';
1616

1717
/**
@@ -66,7 +66,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
6666
/**
6767
* Creates the text editor model with the provided value, modeId (can be comma separated for multiple values) and optional resource URL.
6868
*/
69-
protected createTextEditorModel(value: string | IRawTextProvider, resource?: URI, modeId?: string): TPromise<EditorModel> {
69+
protected createTextEditorModel(value: string | ITextSource, resource?: URI, modeId?: string): TPromise<EditorModel> {
7070
const firstLineText = this.getFirstLineText(value);
7171
const mode = this.getOrCreateMode(this.modeService, modeId, firstLineText);
7272

@@ -76,7 +76,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
7676
});
7777
}
7878

79-
private doCreateTextEditorModel(value: string | IRawTextProvider, mode: TPromise<IMode>, resource: URI): EditorModel {
79+
private doCreateTextEditorModel(value: string | ITextSource, mode: TPromise<IMode>, resource: URI): EditorModel {
8080
let model = resource && this.modelService.getModel(resource);
8181
if (!model) {
8282
model = this.modelService.createModel(value, mode, resource);
@@ -94,7 +94,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
9494
return this;
9595
}
9696

97-
protected getFirstLineText(value: string | IRawTextProvider): string {
97+
protected getFirstLineText(value: string | ITextSource): string {
9898
if (typeof value === 'string') {
9999
const firstLineText = value.substr(0, 100);
100100

@@ -110,7 +110,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
110110

111111
return firstLineText.substr(0, Math.min(crIndex, lfIndex));
112112
} else {
113-
return value.getFirstLine().substr(0, 100);
113+
return value.lines[0].substr(0, 100);
114114
}
115115
}
116116

@@ -126,7 +126,7 @@ export abstract class BaseTextEditorModel extends EditorModel implements ITextEd
126126
/**
127127
* Updates the text editor model with the provided value. If the value is the same as the model has, this is a no-op.
128128
*/
129-
protected updateTextEditorModel(newValue: string | IRawTextProvider): void {
129+
protected updateTextEditorModel(newValue: string | ITextSource): void {
130130
if (!this.textEditorModel) {
131131
return;
132132
}

src/vs/workbench/parts/walkThrough/node/walkThroughContentProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi
7474
return '';
7575
};
7676

77-
const markdown = content.value.getEntireContent().replace(/\r\n/g, '\n'); // TODO: Can marked digest \r\n ?
77+
const markdown = content.value.lines.join('\n');
7878
marked(markdown, { renderer });
7979

8080
const modeId = this.modeService.getModeIdForLanguageName(languageName);

0 commit comments

Comments
 (0)