mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
Merge branch 'master' into diff
This commit is contained in:
@@ -40,7 +40,7 @@ const nodeModules = ['electron', 'original-fs']
|
||||
// Build
|
||||
|
||||
const builtInExtensions = [
|
||||
{ name: 'ms-vscode.node-debug', version: '1.10.11' },
|
||||
{ name: 'ms-vscode.node-debug', version: '1.10.12' },
|
||||
{ name: 'ms-vscode.node-debug2', version: '1.10.0' }
|
||||
];
|
||||
|
||||
|
||||
@@ -17,14 +17,14 @@ export class GitContentProvider {
|
||||
|
||||
private uris = new Set<Uri>();
|
||||
|
||||
constructor(private model: Model, onGitChange: Event<Uri>) {
|
||||
constructor(private model: Model) {
|
||||
this.disposables.push(
|
||||
onGitChange(this.fireChangeEvents, this),
|
||||
model.onDidChangeRepository(this.fireChangeEvents, this),
|
||||
workspace.registerTextDocumentContentProvider('git', this)
|
||||
);
|
||||
}
|
||||
|
||||
private fireChangeEvents(): void {
|
||||
private fireChangeEvents(arg): void {
|
||||
for (let uri of this.uris) {
|
||||
this.onDidChangeEmitter.fire(uri);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import { Model } from './model';
|
||||
import { GitSCMProvider } from './scmProvider';
|
||||
import { CommandCenter } from './commands';
|
||||
import { CheckoutStatusBar, SyncStatusBar } from './statusbar';
|
||||
import { filterEvent, anyEvent } from './util';
|
||||
import { GitContentProvider } from './contentProvider';
|
||||
import { AutoFetcher } from './autofetch';
|
||||
import { MergeDecorator } from './merge';
|
||||
@@ -34,14 +33,10 @@ async function init(disposables: Disposable[]): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const fsWatcher = workspace.createFileSystemWatcher('**');
|
||||
const onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
|
||||
const onGitChange = filterEvent(onWorkspaceChange, uri => /^\.git\//.test(workspace.asRelativePath(uri)));
|
||||
|
||||
const pathHint = workspace.getConfiguration('git').get<string>('path');
|
||||
const info = await findGit(pathHint);
|
||||
const git = new Git({ gitPath: info.path, version: info.version });
|
||||
const model = new Model(git, rootPath, onWorkspaceChange);
|
||||
const model = new Model(git, rootPath);
|
||||
|
||||
outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path));
|
||||
git.onOutput(str => outputChannel.append(str), null, disposables);
|
||||
@@ -49,7 +44,7 @@ async function init(disposables: Disposable[]): Promise<void> {
|
||||
const commitHandler = new CommitController();
|
||||
const commandCenter = new CommandCenter(model, outputChannel);
|
||||
const provider = new GitSCMProvider(model, commandCenter);
|
||||
const contentProvider = new GitContentProvider(model, onGitChange);
|
||||
const contentProvider = new GitContentProvider(model);
|
||||
const checkoutStatusBar = new CheckoutStatusBar(model);
|
||||
const syncStatusBar = new SyncStatusBar(model);
|
||||
const autoFetcher = new AutoFetcher(model);
|
||||
@@ -60,7 +55,6 @@ async function init(disposables: Disposable[]): Promise<void> {
|
||||
commandCenter,
|
||||
provider,
|
||||
contentProvider,
|
||||
fsWatcher,
|
||||
checkoutStatusBar,
|
||||
syncStatusBar,
|
||||
autoFetcher,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import { Uri, EventEmitter, Event, SCMResource, SCMResourceDecorations, SCMResourceGroup, Disposable, window, workspace } from 'vscode';
|
||||
import { Git, Repository, Ref, Branch, Remote, PushOptions, Commit, GitErrorCodes, GitError } from './git';
|
||||
import { anyEvent, eventToPromise, filterEvent, mapEvent, EmptyDisposable, combinedDisposable } from './util';
|
||||
import { anyEvent, eventToPromise, filterEvent, mapEvent, EmptyDisposable, combinedDisposable, dispose } from './util';
|
||||
import { memoize, throttle, debounce } from './decorators';
|
||||
import { watch } from './watch';
|
||||
import * as path from 'path';
|
||||
@@ -222,6 +222,9 @@ export interface CommitOptions {
|
||||
|
||||
export class Model implements Disposable {
|
||||
|
||||
private _onDidChangeRepository = new EventEmitter<Uri>();
|
||||
readonly onDidChangeRepository: Event<Uri> = this._onDidChangeRepository.event;
|
||||
|
||||
private _onDidChangeState = new EventEmitter<State>();
|
||||
readonly onDidChangeState: Event<State> = this._onDidChangeState.event;
|
||||
|
||||
@@ -304,13 +307,18 @@ export class Model implements Disposable {
|
||||
this._onDidChangeResources.fire(this.resources);
|
||||
}
|
||||
|
||||
private onWorkspaceChange: Event<Uri>;
|
||||
private repositoryDisposable: Disposable = EmptyDisposable;
|
||||
private disposables: Disposable[] = [];
|
||||
|
||||
constructor(
|
||||
private git: Git,
|
||||
private rootPath: string,
|
||||
private onWorkspaceChange: Event<Uri>
|
||||
) {
|
||||
const fsWatcher = workspace.createFileSystemWatcher('**');
|
||||
this.onWorkspaceChange = anyEvent(fsWatcher.onDidChange, fsWatcher.onDidCreate, fsWatcher.onDidDelete);
|
||||
this.disposables.push(fsWatcher);
|
||||
|
||||
this.status();
|
||||
}
|
||||
|
||||
@@ -429,7 +437,6 @@ export class Model implements Disposable {
|
||||
await this.run(Operation.Sync, () => this.repository.sync());
|
||||
}
|
||||
|
||||
@throttle
|
||||
async show(ref: string, uri: Uri): Promise<string> {
|
||||
return await this.run(Operation.Show, async () => {
|
||||
const relativePath = path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/');
|
||||
@@ -487,12 +494,13 @@ export class Model implements Disposable {
|
||||
this.repository = this.git.open(repositoryRoot);
|
||||
|
||||
const dotGitPath = path.join(repositoryRoot, '.git');
|
||||
const { event, disposable: watcher } = watch(dotGitPath);
|
||||
const { event: onRawGitChange, disposable: watcher } = watch(dotGitPath);
|
||||
disposables.push(watcher);
|
||||
|
||||
const onGitChange = mapEvent(event, ({ filename }) => Uri.file(path.join(dotGitPath, filename)));
|
||||
const onGitChange = mapEvent(onRawGitChange, ({ filename }) => Uri.file(path.join(dotGitPath, filename)));
|
||||
const onRelevantGitChange = filterEvent(onGitChange, uri => !/\/\.git\/index\.lock$/.test(uri.fsPath));
|
||||
onRelevantGitChange(this.onFSChange, this, disposables);
|
||||
onRelevantGitChange(this._onDidChangeRepository.fire, this._onDidChangeRepository, disposables);
|
||||
|
||||
const onNonGitChange = filterEvent(this.onWorkspaceChange, uri => !/\/\.git\//.test(uri.fsPath));
|
||||
onNonGitChange(this.onFSChange, this, disposables);
|
||||
@@ -603,5 +611,6 @@ export class Model implements Disposable {
|
||||
|
||||
dispose(): void {
|
||||
this.repositoryDisposable.dispose();
|
||||
this.disposables = dispose(this.disposables);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ import URI from './utils/uri';
|
||||
import * as URL from 'url';
|
||||
import Strings = require('./utils/strings');
|
||||
import { JSONDocument, JSONSchema, LanguageSettings, getLanguageService } from 'vscode-json-languageservice';
|
||||
import { ProjectJSONContribution } from './jsoncontributions/projectJSONContribution';
|
||||
import { getLanguageModelCache } from './languageModelCache';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
@@ -119,9 +118,7 @@ let schemaRequestService = (uri: string): Thenable<string> => {
|
||||
let languageService = getLanguageService({
|
||||
schemaRequestService,
|
||||
workspaceContext,
|
||||
contributions: [
|
||||
new ProjectJSONContribution()
|
||||
]
|
||||
contributions: []
|
||||
});
|
||||
|
||||
// The settings interface describes the server relevant settings part
|
||||
|
||||
@@ -1,281 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { MarkedString, CompletionItemKind, CompletionItem, InsertTextFormat } from 'vscode-languageserver';
|
||||
import Strings = require('../utils/strings');
|
||||
import { XHRResponse, getErrorStatusDescription, xhr } from 'request-light';
|
||||
import { JSONWorkerContribution, JSONPath, CompletionsCollector } from 'vscode-json-languageservice';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
|
||||
const FEED_INDEX_URL = 'https://api.nuget.org/v3/index.json';
|
||||
const LIMIT = 30;
|
||||
const RESOLVE_ID = 'ProjectJSONContribution-';
|
||||
|
||||
const CACHE_EXPIRY = 1000 * 60 * 5; // 5 minutes
|
||||
|
||||
interface NugetServices {
|
||||
'SearchQueryService'?: string;
|
||||
'SearchAutocompleteService'?: string;
|
||||
'PackageBaseAddress/3.0.0'?: string;
|
||||
[key: string]: string;
|
||||
}
|
||||
|
||||
export class ProjectJSONContribution implements JSONWorkerContribution {
|
||||
|
||||
private cachedProjects: { [id: string]: { version: string, description: string, time: number } } = {};
|
||||
private cacheSize: number = 0;
|
||||
private nugetIndexPromise: Thenable<NugetServices>;
|
||||
|
||||
public constructor() {
|
||||
}
|
||||
|
||||
private isProjectJSONFile(resource: string): boolean {
|
||||
return Strings.endsWith(resource, '/project.json');
|
||||
}
|
||||
|
||||
private completeWithCache(id: string, item: CompletionItem): boolean {
|
||||
let entry = this.cachedProjects[id];
|
||||
if (entry) {
|
||||
if (new Date().getTime() - entry.time > CACHE_EXPIRY) {
|
||||
delete this.cachedProjects[id];
|
||||
this.cacheSize--;
|
||||
return false;
|
||||
}
|
||||
let insertTextValue = item.insertText;
|
||||
item.detail = entry.version;
|
||||
item.documentation = entry.description;
|
||||
item.insertText = insertTextValue.replace(/\$1/, '${1:' + entry.version + '}');
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private addCached(id: string, version: string, description: string) {
|
||||
this.cachedProjects[id] = { version, description, time: new Date().getTime() };
|
||||
this.cacheSize++;
|
||||
if (this.cacheSize > 50) {
|
||||
let currentTime = new Date().getTime();
|
||||
for (let id in this.cachedProjects) {
|
||||
let entry = this.cachedProjects[id];
|
||||
if (currentTime - entry.time > CACHE_EXPIRY) {
|
||||
delete this.cachedProjects[id];
|
||||
this.cacheSize--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private getNugetIndex(): Thenable<NugetServices> {
|
||||
if (!this.nugetIndexPromise) {
|
||||
this.nugetIndexPromise = this.makeJSONRequest<any>(FEED_INDEX_URL).then(indexContent => {
|
||||
let services: NugetServices = {};
|
||||
if (indexContent && Array.isArray(indexContent.resources)) {
|
||||
let resources = <any[]>indexContent.resources;
|
||||
for (let i = resources.length - 1; i >= 0; i--) {
|
||||
let type = resources[i]['@type'];
|
||||
let id = resources[i]['@id'];
|
||||
if (type && id) {
|
||||
services[type] = id;
|
||||
}
|
||||
}
|
||||
}
|
||||
return services;
|
||||
});
|
||||
}
|
||||
return this.nugetIndexPromise;
|
||||
}
|
||||
|
||||
private getNugetService(serviceType: string): Thenable<string> {
|
||||
return this.getNugetIndex().then(services => {
|
||||
let serviceURL = services[serviceType];
|
||||
if (!serviceURL) {
|
||||
return Promise.reject<string>(localize('json.nugget.error.missingservice', 'NuGet index document is missing service {0}', serviceType));
|
||||
}
|
||||
return serviceURL;
|
||||
});
|
||||
}
|
||||
|
||||
public collectDefaultCompletions(resource: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource)) {
|
||||
let insertText = JSON.stringify({
|
||||
'version': '${1:1.0.0-*}',
|
||||
'dependencies': {},
|
||||
'frameworks': {
|
||||
'net461': {},
|
||||
'netcoreapp1.0': {}
|
||||
}
|
||||
}, null, '\t');
|
||||
result.add({ kind: CompletionItemKind.Class, label: localize('json.project.default', 'Default project.json'), insertText, insertTextFormat: InsertTextFormat.Snippet, documentation: '' });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private makeJSONRequest<T>(url: string): Thenable<T> {
|
||||
return xhr({
|
||||
url: url
|
||||
}).then(success => {
|
||||
if (success.status === 200) {
|
||||
try {
|
||||
return <T>JSON.parse(success.responseText);
|
||||
} catch (e) {
|
||||
return Promise.reject<T>(localize('json.nugget.error.invalidformat', '{0} is not a valid JSON document', url));
|
||||
}
|
||||
}
|
||||
return Promise.reject<T>(localize('json.nugget.error.indexaccess', 'Request to {0} failed: {1}', url, success.responseText));
|
||||
}, (error: XHRResponse) => {
|
||||
return Promise.reject<T>(localize('json.nugget.error.access', 'Request to {0} failed: {1}', url, getErrorStatusDescription(error.status)));
|
||||
});
|
||||
}
|
||||
|
||||
public collectPropertyCompletions(resource: string, location: JSONPath, currentWord: string, addValue: boolean, isLast: boolean, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
|
||||
return this.getNugetService('SearchAutocompleteService').then(service => {
|
||||
let queryUrl: string;
|
||||
if (currentWord.length > 0) {
|
||||
queryUrl = service + '?q=' + encodeURIComponent(currentWord) + '&take=' + LIMIT;
|
||||
} else {
|
||||
queryUrl = service + '?take=' + LIMIT;
|
||||
}
|
||||
return this.makeJSONRequest<any>(queryUrl).then(resultObj => {
|
||||
if (Array.isArray(resultObj.data)) {
|
||||
let results = <any[]>resultObj.data;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let name = results[i];
|
||||
let insertText = JSON.stringify(name);
|
||||
if (addValue) {
|
||||
insertText += ': "$1"';
|
||||
if (!isLast) {
|
||||
insertText += ',';
|
||||
}
|
||||
}
|
||||
let item: CompletionItem = { kind: CompletionItemKind.Property, label: name, insertText: insertText, insertTextFormat: InsertTextFormat.Snippet, filterText: JSON.stringify(name) };
|
||||
if (!this.completeWithCache(name, item)) {
|
||||
item.data = RESOLVE_ID + name;
|
||||
}
|
||||
result.add(item);
|
||||
}
|
||||
if (results.length === LIMIT) {
|
||||
result.setAsIncomplete();
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
result.error(error);
|
||||
});
|
||||
}, error => {
|
||||
result.error(error);
|
||||
});
|
||||
};
|
||||
return null;
|
||||
}
|
||||
|
||||
public collectValueCompletions(resource: string, location: JSONPath, currentKey: string, result: CompletionsCollector): Thenable<any> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies']) || matches(location, ['frameworks', '*', 'dependencies']) || matches(location, ['frameworks', '*', 'frameworkAssemblies']))) {
|
||||
return this.getNugetService('PackageBaseAddress/3.0.0').then(service => {
|
||||
let queryUrl = service + currentKey + '/index.json';
|
||||
return this.makeJSONRequest<any>(queryUrl).then(obj => {
|
||||
if (Array.isArray(obj.versions)) {
|
||||
let results = <any[]>obj.versions;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let curr = results[i];
|
||||
let name = JSON.stringify(curr);
|
||||
let label = name;
|
||||
let documentation = '';
|
||||
result.add({ kind: CompletionItemKind.Class, label: label, insertText: name, documentation: documentation });
|
||||
}
|
||||
if (results.length === LIMIT) {
|
||||
result.setAsIncomplete();
|
||||
}
|
||||
}
|
||||
}, error => {
|
||||
result.error(error);
|
||||
});
|
||||
}, error => {
|
||||
result.error(error);
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getInfoContribution(resource: string, location: JSONPath): Thenable<MarkedString[]> {
|
||||
if (this.isProjectJSONFile(resource) && (matches(location, ['dependencies', '*']) || matches(location, ['frameworks', '*', 'dependencies', '*']) || matches(location, ['frameworks', '*', 'frameworkAssemblies', '*']))) {
|
||||
let pack = <string>location[location.length - 1];
|
||||
|
||||
return this.getNugetService('SearchQueryService').then(service => {
|
||||
let queryUrl = service + '?q=' + encodeURIComponent(pack) + '&take=' + 5;
|
||||
return this.makeJSONRequest<any>(queryUrl).then(resultObj => {
|
||||
let htmlContent: MarkedString[] = [];
|
||||
htmlContent.push(localize('json.nugget.package.hover', '{0}', pack));
|
||||
if (Array.isArray(resultObj.data)) {
|
||||
let results = <any[]>resultObj.data;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let res = results[i];
|
||||
this.addCached(res.id, res.version, res.description);
|
||||
if (res.id === pack) {
|
||||
if (res.description) {
|
||||
htmlContent.push(MarkedString.fromPlainText(res.description));
|
||||
}
|
||||
if (res.version) {
|
||||
htmlContent.push(MarkedString.fromPlainText(localize('json.nugget.version.hover', 'Latest version: {0}', res.version)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return htmlContent;
|
||||
}, (error) => {
|
||||
return null;
|
||||
});
|
||||
}, (error) => {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public resolveSuggestion(item: CompletionItem): Thenable<CompletionItem> {
|
||||
if (item.data && Strings.startsWith(item.data, RESOLVE_ID)) {
|
||||
let pack = item.data.substring(RESOLVE_ID.length);
|
||||
if (this.completeWithCache(pack, item)) {
|
||||
return Promise.resolve(item);
|
||||
}
|
||||
return this.getNugetService('SearchQueryService').then(service => {
|
||||
let queryUrl = service + '?q=' + encodeURIComponent(pack) + '&take=' + 10;
|
||||
return this.makeJSONRequest<CompletionItem>(queryUrl).then(resultObj => {
|
||||
let itemResolved = false;
|
||||
if (Array.isArray(resultObj.data)) {
|
||||
let results = <any[]>resultObj.data;
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
let curr = results[i];
|
||||
this.addCached(curr.id, curr.version, curr.description);
|
||||
if (curr.id === pack) {
|
||||
this.completeWithCache(pack, item);
|
||||
itemResolved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return itemResolved ? item : null;
|
||||
});
|
||||
});
|
||||
};
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function matches(segments: JSONPath, pattern: string[]) {
|
||||
let k = 0;
|
||||
for (let i = 0; k < pattern.length && i < segments.length; i++) {
|
||||
if (pattern[k] === segments[i] || pattern[k] === '*') {
|
||||
k++;
|
||||
} else if (pattern[k] !== '**') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return k === pattern.length;
|
||||
}
|
||||
+3
-3
@@ -13,9 +13,9 @@
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz"
|
||||
},
|
||||
"typescript": {
|
||||
"version": "typescript@2.2.1-insiders.20170209",
|
||||
"from": "typescript@typescript@2.2.1-insiders.20170209",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.1-insiders.20170209.tgz"
|
||||
"version": "typescript@2.2.1-insiders.20170216",
|
||||
"from": "typescript@typescript@2.2.1-insiders.20170216",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.2.1-insiders.20170216.tgz"
|
||||
},
|
||||
"vscode-extension-telemetry": {
|
||||
"version": "0.0.5",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"semver": "4.3.6",
|
||||
"vscode-extension-telemetry": "^0.0.5",
|
||||
"vscode-nls": "^2.0.1",
|
||||
"typescript": "typescript@2.2.1-insiders.20170209"
|
||||
"typescript": "typescript@2.2.1-insiders.20170216"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^7.0.4",
|
||||
|
||||
@@ -27,10 +27,10 @@ export default class TypeScriptCodeActionProvider implements CodeActionProvider
|
||||
private supportedCodeActions: Promise<NumberSet>;
|
||||
|
||||
constructor(
|
||||
private client: ITypescriptServiceClient,
|
||||
modeId: string
|
||||
private readonly client: ITypescriptServiceClient,
|
||||
mode: string
|
||||
) {
|
||||
this.commandId = `typescript.codeActions.${modeId}`;
|
||||
this.commandId = `_typescript.applyCodeAction.${mode}`;
|
||||
this.supportedCodeActions = client.execute('getSupportedCodeFixes', null, undefined)
|
||||
.then(response => response.body || [])
|
||||
.then(codes => codes.map(code => +code).filter(code => !isNaN(code)))
|
||||
|
||||
@@ -70,40 +70,41 @@ export default class TypeScriptReferencesCodeLensProvider implements CodeLensPro
|
||||
|
||||
resolveCodeLens(inputCodeLens: CodeLens, token: CancellationToken): Promise<CodeLens> {
|
||||
const codeLens = inputCodeLens as ReferencesCodeLens;
|
||||
if (!codeLens.document) {
|
||||
return Promise.reject<CodeLens>(codeLens);
|
||||
}
|
||||
const args: Proto.FileLocationRequestArgs = {
|
||||
file: codeLens.file,
|
||||
line: codeLens.range.start.line + 1,
|
||||
offset: codeLens.range.start.character + 1
|
||||
};
|
||||
return this.client.execute('references', args, token).then(response => {
|
||||
if (response && response.body) {
|
||||
// Exclude original definition from references
|
||||
const locations = response.body.refs
|
||||
.filter(reference =>
|
||||
!(reference.start.line === codeLens.range.start.line + 1
|
||||
&& reference.start.offset === codeLens.range.start.character + 1))
|
||||
.map(reference =>
|
||||
new Location(this.client.asUrl(reference.file),
|
||||
new Range(
|
||||
new Position(reference.start.line - 1, reference.start.offset - 1),
|
||||
new Position(reference.end.line - 1, reference.end.offset - 1))));
|
||||
codeLens.command = {
|
||||
title: locations.length + ' ' + (locations.length === 1 ? localize('oneReferenceLabel', 'reference') : localize('manyReferenceLabel', 'references')),
|
||||
command: 'editor.action.showReferences',
|
||||
arguments: [codeLens.document, codeLens.range.start, locations]
|
||||
};
|
||||
return Promise.resolve(codeLens);
|
||||
if (!response || !response.body) {
|
||||
throw codeLens;
|
||||
}
|
||||
return Promise.reject<CodeLens>(codeLens);
|
||||
|
||||
// Exclude original definition from references
|
||||
const locations = response.body.refs
|
||||
.filter(reference =>
|
||||
!(reference.start.line === codeLens.range.start.line + 1
|
||||
&& reference.start.offset === codeLens.range.start.character + 1))
|
||||
.map(reference =>
|
||||
new Location(this.client.asUrl(reference.file),
|
||||
new Range(
|
||||
reference.start.line - 1, reference.start.offset - 1,
|
||||
reference.end.line - 1, reference.end.offset - 1)));
|
||||
|
||||
codeLens.command = {
|
||||
title: locations.length === 1
|
||||
? localize('oneReferenceLabel', '1 reference')
|
||||
: localize('manyReferenceLabel', '{0} references', locations.length),
|
||||
command: 'editor.action.showReferences',
|
||||
arguments: [codeLens.document, codeLens.range.start, locations]
|
||||
};
|
||||
return codeLens;
|
||||
}).catch(() => {
|
||||
codeLens.command = {
|
||||
title: localize('referenceErrorLabel', 'Could not determine references'),
|
||||
command: ''
|
||||
};
|
||||
return Promise.resolve(codeLens);
|
||||
return codeLens;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -115,8 +116,8 @@ export default class TypeScriptReferencesCodeLensProvider implements CodeLensPro
|
||||
const span = item.spans && item.spans[0];
|
||||
if (span) {
|
||||
const range = new Range(
|
||||
new Position(span.start.line - 1, span.start.offset - 1),
|
||||
new Position(span.end.line - 1, span.end.offset - 1));
|
||||
span.start.line - 1, span.start.offset - 1,
|
||||
span.end.line - 1, span.end.offset - 1);
|
||||
|
||||
// TODO: TS currently requires the position for 'references 'to be inside of the identifer
|
||||
// Massage the range to make sure this is the case
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* ------------------------------------------------------------------------------------------ */
|
||||
'use strict';
|
||||
|
||||
import { env, languages, commands, workspace, window, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, DocumentFilter, Disposable, Uri, MessageItem, TextEditor } from 'vscode';
|
||||
import { env, languages, commands, workspace, window, ExtensionContext, Memento, IndentAction, Diagnostic, DiagnosticCollection, Range, Disposable, Uri, MessageItem, TextEditor } from 'vscode';
|
||||
|
||||
// This must be the first statement otherwise modules might got loaded with
|
||||
// the wrong locale.
|
||||
@@ -45,7 +45,6 @@ import * as BuildStatus from './utils/buildStatus';
|
||||
import * as ProjectStatus from './utils/projectStatus';
|
||||
import TypingsStatus, { AtaProgressReporter } from './utils/typingsStatus';
|
||||
import * as VersionStatus from './utils/versionStatus';
|
||||
import ProjectConfigStatus from './utils/projectConfigStatus';
|
||||
|
||||
interface LanguageDescription {
|
||||
id: string;
|
||||
@@ -126,9 +125,7 @@ export function activate(context: ExtensionContext): void {
|
||||
context.subscriptions.push(commands.registerCommand('javascript.goToProjectConfig', goToProjectConfig.bind(null, false)));
|
||||
|
||||
window.onDidChangeActiveTextEditor(VersionStatus.showHideStatus, null, context.subscriptions);
|
||||
|
||||
client.onReady().then(() => {
|
||||
context.subscriptions.push(new ProjectConfigStatus(client));
|
||||
context.subscriptions.push(ProjectStatus.create(client,
|
||||
path => new Promise(resolve => setTimeout(() => resolve(clientHost.handles(path)), 750)),
|
||||
context.workspaceState));
|
||||
@@ -153,15 +150,14 @@ class LanguageProvider {
|
||||
private typingsStatus: TypingsStatus;
|
||||
private referenceCodeLensProvider: ReferenceCodeLensProvider;
|
||||
|
||||
private _validate: boolean;
|
||||
private _validate: boolean = true;
|
||||
|
||||
constructor(
|
||||
private client: TypeScriptServiceClient,
|
||||
private description: LanguageDescription
|
||||
private readonly client: TypeScriptServiceClient,
|
||||
private readonly description: LanguageDescription
|
||||
) {
|
||||
this.extensions = Object.create(null);
|
||||
description.extensions.forEach(extension => this.extensions[extension] = true);
|
||||
this._validate = true;
|
||||
|
||||
this.bufferSyncSupport = new BufferSyncSupport(client, description.modeIds, {
|
||||
delete: (file: string) => {
|
||||
@@ -186,54 +182,48 @@ class LanguageProvider {
|
||||
}
|
||||
|
||||
private registerProviders(client: TypeScriptServiceClient): void {
|
||||
const selector = this.description.modeIds;
|
||||
const config = workspace.getConfiguration(this.id);
|
||||
|
||||
this.completionItemProvider = new CompletionItemProvider(client, this.typingsStatus);
|
||||
this.completionItemProvider.updateConfiguration();
|
||||
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
|
||||
|
||||
let hoverProvider = new HoverProvider(client);
|
||||
let definitionProvider = new DefinitionProvider(client);
|
||||
let implementationProvider = new ImplementationProvider(client);
|
||||
const typeDefinitionProvider = new TypeDefintionProvider(client);
|
||||
let documentHighlightProvider = new DocumentHighlightProvider(client);
|
||||
let referenceProvider = new ReferenceProvider(client);
|
||||
let documentSymbolProvider = new DocumentSymbolProvider(client);
|
||||
let signatureHelpProvider = new SignatureHelpProvider(client);
|
||||
let renameProvider = new RenameProvider(client);
|
||||
this.formattingProvider = new FormattingProvider(client);
|
||||
this.formattingProvider.updateConfiguration(config);
|
||||
languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n');
|
||||
if (this.formattingProvider.isEnabled()) {
|
||||
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(this.description.modeIds, this.formattingProvider);
|
||||
this.formattingProviderRegistration = languages.registerDocumentRangeFormattingEditProvider(selector, this.formattingProvider);
|
||||
}
|
||||
|
||||
this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
|
||||
this.referenceCodeLensProvider.updateConfiguration();
|
||||
languages.registerHoverProvider(selector, new HoverProvider(client));
|
||||
languages.registerDefinitionProvider(selector, new DefinitionProvider(client));
|
||||
languages.registerDocumentHighlightProvider(selector, new DocumentHighlightProvider(client));
|
||||
languages.registerReferenceProvider(selector, new ReferenceProvider(client));
|
||||
languages.registerDocumentSymbolProvider(selector, new DocumentSymbolProvider(client));
|
||||
languages.registerSignatureHelpProvider(selector, new SignatureHelpProvider(client), '(', ',');
|
||||
languages.registerRenameProvider(selector, new RenameProvider(client));
|
||||
|
||||
if (client.apiVersion.has206Features()) {
|
||||
languages.registerCodeLensProvider(this.description.modeIds, this.referenceCodeLensProvider);
|
||||
this.referenceCodeLensProvider = new ReferenceCodeLensProvider(client);
|
||||
this.referenceCodeLensProvider.updateConfiguration();
|
||||
languages.registerCodeLensProvider(selector, this.referenceCodeLensProvider);
|
||||
}
|
||||
|
||||
if (client.apiVersion.has213Features()) {
|
||||
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, this.description.id));
|
||||
}
|
||||
|
||||
if (client.apiVersion.has220Features()) {
|
||||
languages.registerImplementationProvider(selector, new ImplementationProvider(client));
|
||||
}
|
||||
|
||||
if (client.apiVersion.has213Features()) {
|
||||
languages.registerTypeDefinitionProvider(selector, new TypeDefintionProvider(client));
|
||||
}
|
||||
|
||||
this.description.modeIds.forEach(modeId => {
|
||||
const selector: DocumentFilter = modeId;
|
||||
languages.registerCompletionItemProvider(selector, this.completionItemProvider, '.');
|
||||
languages.registerHoverProvider(selector, hoverProvider);
|
||||
languages.registerDefinitionProvider(selector, definitionProvider);
|
||||
if (client.apiVersion.has220Features()) {
|
||||
// TODO: TS 2.1.5 returns incorrect results for implementation locations.
|
||||
languages.registerImplementationProvider(selector, implementationProvider);
|
||||
}
|
||||
if (client.apiVersion.has213Features()) {
|
||||
languages.registerTypeDefinitionProvider(selector, typeDefinitionProvider);
|
||||
}
|
||||
languages.registerDocumentHighlightProvider(selector, documentHighlightProvider);
|
||||
languages.registerReferenceProvider(selector, referenceProvider);
|
||||
languages.registerDocumentSymbolProvider(selector, documentSymbolProvider);
|
||||
languages.registerSignatureHelpProvider(selector, signatureHelpProvider, '(', ',');
|
||||
languages.registerRenameProvider(selector, renameProvider);
|
||||
languages.registerOnTypeFormattingEditProvider(selector, this.formattingProvider, ';', '}', '\n');
|
||||
languages.registerWorkspaceSymbolProvider(new WorkspaceSymbolProvider(client, modeId));
|
||||
if (client.apiVersion.has213Features()) {
|
||||
languages.registerCodeActionsProvider(selector, new CodeActionProvider(client, modeId));
|
||||
}
|
||||
|
||||
languages.setLanguageConfiguration(modeId, {
|
||||
indentationRules: {
|
||||
@@ -249,18 +239,15 @@ class LanguageProvider {
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
afterText: /^\s*\*\/$/,
|
||||
action: { indentAction: IndentAction.IndentOutdent, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
}, {
|
||||
// e.g. /** ...|
|
||||
beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/,
|
||||
action: { indentAction: IndentAction.None, appendText: ' * ' }
|
||||
},
|
||||
{
|
||||
}, {
|
||||
// e.g. * ...|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*(\ ([^\*]|\*(?!\/))*)?$/,
|
||||
action: { indentAction: IndentAction.None, appendText: '* ' }
|
||||
},
|
||||
{
|
||||
}, {
|
||||
// e.g. */|
|
||||
beforeText: /^(\t|(\ \ ))*\ \*\/\s*$/,
|
||||
action: { indentAction: IndentAction.None, removeText: 1 }
|
||||
@@ -378,17 +365,22 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
|
||||
private languages: LanguageProvider[];
|
||||
private languagePerId: ObjectMap<LanguageProvider>;
|
||||
|
||||
constructor(descriptions: LanguageDescription[], storagePath: string | undefined, globalState: Memento, workspaceState: Memento) {
|
||||
let handleProjectCreateOrDelete = () => {
|
||||
constructor(
|
||||
descriptions: LanguageDescription[],
|
||||
storagePath: string | undefined,
|
||||
globalState: Memento,
|
||||
workspaceState: Memento
|
||||
) {
|
||||
const handleProjectCreateOrDelete = () => {
|
||||
this.client.execute('reloadProjects', null, false);
|
||||
this.triggerAllDiagnostics();
|
||||
};
|
||||
let handleProjectChange = () => {
|
||||
const handleProjectChange = () => {
|
||||
setTimeout(() => {
|
||||
this.triggerAllDiagnostics();
|
||||
}, 1500);
|
||||
};
|
||||
let watcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json');
|
||||
const watcher = workspace.createFileSystemWatcher('**/[tj]sconfig.json');
|
||||
watcher.onDidCreate(handleProjectCreateOrDelete);
|
||||
watcher.onDidDelete(handleProjectCreateOrDelete);
|
||||
watcher.onDidChange(handleProjectChange);
|
||||
@@ -397,7 +389,7 @@ class TypeScriptServiceClientHost implements ITypescriptServiceClientHost {
|
||||
this.languages = [];
|
||||
this.languagePerId = Object.create(null);
|
||||
descriptions.forEach(description => {
|
||||
let manager = new LanguageProvider(this.client, description);
|
||||
const manager = new LanguageProvider(this.client, description);
|
||||
this.languages.push(manager);
|
||||
this.languagePerId[description.id] = manager;
|
||||
});
|
||||
|
||||
@@ -124,6 +124,7 @@ export default class TypeScriptServiceClient implements ITypescriptServiceClient
|
||||
private _apiVersion: API;
|
||||
private telemetryReporter: TelemetryReporter;
|
||||
|
||||
|
||||
constructor(host: ITypescriptServiceClientHost, storagePath: string | undefined, globalState: Memento, private workspaceState: Memento) {
|
||||
this.host = host;
|
||||
this.storagePath = storagePath;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TextEditor, Position, Range, Selection } from 'vscode';
|
||||
|
||||
import { ITypescriptServiceClient } from '../typescriptService';
|
||||
|
||||
import { FileLocationRequestArgs } from '../protocol';
|
||||
import { FileLocationRequestArgs, DocCommandTemplateResponse } from '../protocol';
|
||||
|
||||
export default class JsDocCompletionHelper {
|
||||
|
||||
@@ -51,51 +51,47 @@ export default class JsDocCompletionHelper {
|
||||
return false;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
const timer = setTimeout(() => {
|
||||
cancelled = true;
|
||||
}, 250);
|
||||
|
||||
const args: FileLocationRequestArgs = {
|
||||
file: file,
|
||||
line: start.line + 1,
|
||||
offset: start.character + 1
|
||||
};
|
||||
|
||||
return this.client.execute('docCommentTemplate', args)
|
||||
.then(res => {
|
||||
clearTimeout(timer);
|
||||
if (cancelled || !res || !res.body) {
|
||||
return false;
|
||||
}
|
||||
const commentText = res.body.newText;
|
||||
return editor.edit(
|
||||
edits => edits.insert(start, commentText),
|
||||
{ undoStopBefore: false, undoStopAfter: true });
|
||||
}, () => {
|
||||
clearTimeout(timer);
|
||||
return false;
|
||||
return Promise.race([
|
||||
this.client.execute('docCommentTemplate', args),
|
||||
new Promise((_, reject) => {
|
||||
setTimeout(reject, 250);
|
||||
})
|
||||
.then(didInsertComment => {
|
||||
if (didInsertComment) {
|
||||
const newCursorPosition = new Position(start.line + 1, editor.document.lineAt(start.line + 1).text.length);
|
||||
editor.selection = new Selection(newCursorPosition, newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
]).then((res: DocCommandTemplateResponse) => {
|
||||
if (!res || !res.body) {
|
||||
return false;
|
||||
}
|
||||
const commentText = res.body.newText;
|
||||
return editor.edit(
|
||||
edits => edits.insert(start, commentText),
|
||||
{ undoStopBefore: false, undoStopAfter: true });
|
||||
}, () => {
|
||||
return false;
|
||||
}).then(didInsertComment => {
|
||||
if (didInsertComment) {
|
||||
const newCursorPosition = new Position(start.line + 1, editor.document.lineAt(start.line + 1).text.length);
|
||||
editor.selection = new Selection(newCursorPosition, newCursorPosition);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Revert to the original line content and restore position
|
||||
return editor.edit(
|
||||
edits => {
|
||||
edits.insert(start, prefix[1] + suffix[0]);
|
||||
}, {
|
||||
undoStopBefore: false,
|
||||
undoStopAfter: true
|
||||
}
|
||||
).then(() => {
|
||||
editor.selection = new Selection(position, position);
|
||||
return false;
|
||||
});
|
||||
// Revert to the original line content and restore position
|
||||
return editor.edit(
|
||||
edits => {
|
||||
edits.insert(start, prefix[1] + suffix[0]);
|
||||
}, {
|
||||
undoStopBefore: false,
|
||||
undoStopAfter: true
|
||||
}
|
||||
).then(() => {
|
||||
editor.selection = new Selection(position, position);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { ITypescriptServiceClient } from '../typescriptService';
|
||||
|
||||
import { loadMessageBundle } from 'vscode-nls';
|
||||
const localize = loadMessageBundle();
|
||||
|
||||
export default class ProjectConfigStatus implements vscode.Disposable {
|
||||
private entry: vscode.StatusBarItem;
|
||||
private subscription: vscode.Disposable;
|
||||
|
||||
constructor(private client: ITypescriptServiceClient) {
|
||||
this.entry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE);
|
||||
this.entry.color = 'white';
|
||||
this.entry.command = 'typescript.goToProjectConfig';
|
||||
|
||||
this.subscription = vscode.window.onDidChangeActiveTextEditor(editor => this.showHideStatus(editor));
|
||||
if (vscode.window.activeTextEditor) {
|
||||
this.showHideStatus(vscode.window.activeTextEditor);
|
||||
}
|
||||
}
|
||||
|
||||
private showHideStatus(editor: vscode.TextEditor | undefined) {
|
||||
editor = editor || vscode.window.activeTextEditor;
|
||||
if (!editor || !vscode.workspace.rootPath) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
const doc = editor.document;
|
||||
const isTypeScript = !!(vscode.languages.match('typescript', doc) || vscode.languages.match('typescriptreact', doc));
|
||||
if (isTypeScript || vscode.languages.match('javascript', doc) || vscode.languages.match('javascriptreact', doc)) {
|
||||
this.showStatusForResource(doc.uri, isTypeScript);
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
private showStatusForResource(resource: vscode.Uri, isTypeScript: boolean) {
|
||||
const file = this.client.normalizePath(resource);
|
||||
if (!file) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
return this.client.execute('projectInfo', { file, needFileNameList: false }).then(res => {
|
||||
if (!res || !res.body || !res.body.configFileName) {
|
||||
this.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const { configFileName } = res.body;
|
||||
this.entry.tooltip = configFileName;
|
||||
this.entry.command = isTypeScript ? 'typescript.goToProjectConfig' : 'javascript.goToProjectConfig';
|
||||
|
||||
if (configFileName.toLowerCase().endsWith('tsconfig.json')) {
|
||||
this.entry.text = 'tsconfig';
|
||||
} else if (configFileName.toLowerCase().endsWith('jsconfig.json')) {
|
||||
this.entry.text = 'jsconfig';
|
||||
} else {
|
||||
this.entry.text = isTypeScript
|
||||
? localize('typescript.projectConfigStatus.noTypeScriptProject', 'No TS Project')
|
||||
: localize('typescript.projectConfigStatus.noJavaScriptProject', 'No JS Project');
|
||||
}
|
||||
this.entry.show();
|
||||
});
|
||||
}
|
||||
|
||||
private hide() {
|
||||
this.entry.hide();
|
||||
this.entry.text = '';
|
||||
this.entry.tooltip = '';
|
||||
}
|
||||
|
||||
dispose() {
|
||||
this.entry.dispose();
|
||||
this.subscription.dispose();
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import vscode = require('vscode');
|
||||
|
||||
const versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE + 1);
|
||||
const versionBarEntry = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right, Number.MIN_VALUE);
|
||||
|
||||
export function showHideStatus() {
|
||||
if (!versionBarEntry) {
|
||||
|
||||
Generated
+1
-1
@@ -427,7 +427,7 @@
|
||||
"xterm": {
|
||||
"version": "2.3.0",
|
||||
"from": "Tyriar/xterm.js#vscode-release/1.10",
|
||||
"resolved": "git+https://github.com/Tyriar/xterm.js.git#5513303451202b0135601a2f026602ed391b3906"
|
||||
"resolved": "git+https://github.com/Tyriar/xterm.js.git#a1543c16baee7ee35f6e3d6cd434c35afcf3b609"
|
||||
},
|
||||
"yauzl": {
|
||||
"version": "2.3.1",
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.10.0",
|
||||
"electronVersion": "1.4.6",
|
||||
"distro": "4d6820ed4c9ffeac4febc40ac77bc391c8700a0d",
|
||||
"distro": "fdc80c6b4d95e5f961bd3f85b927693915550942",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
||||
@@ -31,8 +31,38 @@ if [ "@@NAME@@" != "code-oss" ]; then
|
||||
APT_DIR=$(get_apt_config_value Dir)
|
||||
APT_ETC=$APT_DIR$(get_apt_config_value Dir::Etc)
|
||||
APT_SOURCE_PARTS=$APT_ETC/$(get_apt_config_value Dir::Etc::sourceparts)
|
||||
CODE_SOURCE_LIST=$APT_SOURCE_PARTS/vscode.list
|
||||
CODE_SOURCE_PART=$APT_SOURCE_PARTS/vscode.list
|
||||
|
||||
rm -f $CODE_SOURCE_LIST
|
||||
# echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_LIST
|
||||
fi
|
||||
APT_TRUSTED_PARTS=$APT_ETC/$(get_apt_config_value Dir::Etc::trustedparts)
|
||||
CODE_TRUSTED_PART=$APT_TRUSTED_PARTS/microsoft.gpg
|
||||
|
||||
# Sourced from https://packages.microsoft.com/keys/microsoft.asc
|
||||
if [ ! -f $CODE_TRUSTED_PART ]; then
|
||||
echo "-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.7 (GNU/Linux)
|
||||
|
||||
mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT
|
||||
LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV
|
||||
7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag
|
||||
OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j
|
||||
H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr
|
||||
M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs
|
||||
ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATUEEwEC
|
||||
AB8FAlYxWIwCGwMGCwkIBwMCBBUCCAMDFgIBAh4BAheAAAoJEOs+lK2+EinPGpsH
|
||||
/32vKy29Hg51H9dfFJMx0/a/F+5vKeCeVqimvyTM04C+XENNuSbYZ3eRPHGHFLqe
|
||||
MNGxsfb7C7ZxEeW7J/vSzRgHxm7ZvESisUYRFq2sgkJ+HFERNrqfci45bdhmrUsy
|
||||
7SWw9ybxdFOkuQoyKD3tBmiGfONQMlBaOMWdAsic965rvJsd5zYaZZFI1UwTkFXV
|
||||
KJt3bp3Ngn1vEYXwijGTa+FXz6GLHueJwF0I7ug34DgUkAFvAs8Hacr2DRYxL5RJ
|
||||
XdNgj4Jd2/g6T9InmWT0hASljur+dJnzNiNCkbn9KbX7J/qK1IbR8y560yRmFsU+
|
||||
NdCFTW7wY0Fb1fWJ+/KTsC4=
|
||||
=J6gs
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
" | gpg --dearmor > microsoft.gpg
|
||||
mv microsoft.gpg $CODE_TRUSTED_PART
|
||||
fi
|
||||
|
||||
# Install repository source list if it does not already exist
|
||||
if [ ! -f $CODE_SOURCE_PART ]; then
|
||||
echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > $CODE_SOURCE_PART
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -34,6 +34,10 @@ set ELECTRON_ENABLE_LOGGING=1
|
||||
set ELECTRON_ENABLE_STACK_DUMPING=1
|
||||
|
||||
:: Launch Code
|
||||
|
||||
:: Use the following to get v8 tracing:
|
||||
:: %CODE% --js-flags="--trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces" . %*
|
||||
|
||||
%CODE% . %*
|
||||
popd
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { EventEmitter } from 'vs/base/common/eventEmitter';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isObject } from 'vs/base/common/types';
|
||||
import { isChrome, isWebKit } from 'vs/base/browser/browser';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { IMouseEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
@@ -211,6 +211,7 @@ export function addDisposableListener(node: Element | Window | Document, type: s
|
||||
|
||||
export interface IAddStandardDisposableListenerSignature {
|
||||
(node: HTMLElement, type: 'click', handler: (event: IMouseEvent) => void, useCapture?: boolean): IDisposable;
|
||||
(node: HTMLElement, type: 'mousedown', handler: (event: IMouseEvent) => void, useCapture?: boolean): IDisposable;
|
||||
(node: HTMLElement, type: 'keydown', handler: (event: IKeyboardEvent) => void, useCapture?: boolean): IDisposable;
|
||||
(node: HTMLElement, type: 'keypress', handler: (event: IKeyboardEvent) => void, useCapture?: boolean): IDisposable;
|
||||
(node: HTMLElement, type: 'keyup', handler: (event: IKeyboardEvent) => void, useCapture?: boolean): IDisposable;
|
||||
@@ -229,7 +230,7 @@ function _wrapAsStandardKeyboardEvent(handler: (e: IKeyboardEvent) => void): (e:
|
||||
export let addStandardDisposableListener: IAddStandardDisposableListenerSignature = function addStandardDisposableListener(node: HTMLElement, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable {
|
||||
let wrapHandler = handler;
|
||||
|
||||
if (type === 'click') {
|
||||
if (type === 'click' || type === 'mousedown') {
|
||||
wrapHandler = _wrapAsStandardMouseEvent(handler);
|
||||
} else if (type === 'keydown' || type === 'keypress' || type === 'keyup') {
|
||||
wrapHandler = _wrapAsStandardKeyboardEvent(handler);
|
||||
@@ -377,21 +378,7 @@ class AnimationFrameQueueItem implements IDisposable {
|
||||
|
||||
if (!animFrameRequested) {
|
||||
animFrameRequested = true;
|
||||
|
||||
// TODO@Alex: also check if it is electron
|
||||
if (isChrome) {
|
||||
let handle: number;
|
||||
_animationFrame.request(function () {
|
||||
clearTimeout(handle);
|
||||
animationFrameRunner();
|
||||
});
|
||||
// This is a fallback in-case chrome dropped
|
||||
// the request for an animation frame. This
|
||||
// is sick but was spotted in the wild
|
||||
handle = setTimeout(animationFrameRunner, 1000);
|
||||
} else {
|
||||
_animationFrame.request(animationFrameRunner);
|
||||
}
|
||||
_animationFrame.request(animationFrameRunner);
|
||||
}
|
||||
|
||||
return item;
|
||||
@@ -797,9 +784,9 @@ export const EventType = {
|
||||
DROP: 'drop',
|
||||
DRAG_END: 'dragend',
|
||||
// Animation
|
||||
ANIMATION_START: isWebKit ? 'webkitAnimationStart' : 'animationstart',
|
||||
ANIMATION_END: isWebKit ? 'webkitAnimationEnd' : 'animationend',
|
||||
ANIMATION_ITERATION: isWebKit ? 'webkitAnimationIteration' : 'animationiteration'
|
||||
ANIMATION_START: browser.isWebKit ? 'webkitAnimationStart' : 'animationstart',
|
||||
ANIMATION_END: browser.isWebKit ? 'webkitAnimationEnd' : 'animationend',
|
||||
ANIMATION_ITERATION: browser.isWebKit ? 'webkitAnimationIteration' : 'animationiteration'
|
||||
};
|
||||
|
||||
export interface EventLike {
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
|
||||
export abstract class FastDomNode {
|
||||
export abstract class FastDomNode<T extends HTMLElement> {
|
||||
|
||||
private _domNode: HTMLElement;
|
||||
private _domNode: T;
|
||||
private _maxWidth: number;
|
||||
private _width: number;
|
||||
private _height: number;
|
||||
@@ -26,11 +26,11 @@ export abstract class FastDomNode {
|
||||
private _visibility: string;
|
||||
private _transform: string;
|
||||
|
||||
public get domNode(): HTMLElement {
|
||||
public get domNode(): T {
|
||||
return this._domNode;
|
||||
}
|
||||
|
||||
constructor(domNode: HTMLElement) {
|
||||
constructor(domNode: T) {
|
||||
this._domNode = domNode;
|
||||
this._maxWidth = -1;
|
||||
this._width = -1;
|
||||
@@ -183,21 +183,21 @@ export abstract class FastDomNode {
|
||||
this._setTransform(this._domNode, this._transform);
|
||||
}
|
||||
|
||||
protected abstract _setTransform(domNode: HTMLElement, transform: string): void;
|
||||
protected abstract _setTransform(domNode: T, transform: string): void;
|
||||
|
||||
public setAttribute(name: string, value: string): void {
|
||||
this._domNode.setAttribute(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
class WebKitFastDomNode extends FastDomNode {
|
||||
protected _setTransform(domNode: HTMLElement, transform: string): void {
|
||||
class WebKitFastDomNode<T extends HTMLElement> extends FastDomNode<T> {
|
||||
protected _setTransform(domNode: T, transform: string): void {
|
||||
(<any>domNode.style).webkitTransform = transform;
|
||||
}
|
||||
}
|
||||
|
||||
class StandardFastDomNode extends FastDomNode {
|
||||
protected _setTransform(domNode: HTMLElement, transform: string): void {
|
||||
class StandardFastDomNode<T extends HTMLElement> extends FastDomNode<T> {
|
||||
protected _setTransform(domNode: T, transform: string): void {
|
||||
domNode.style.transform = transform;
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ let useWebKitFastDomNode = false;
|
||||
useWebKitFastDomNode = true;
|
||||
}
|
||||
})();
|
||||
export function createFastDomNode(domNode: HTMLElement): FastDomNode {
|
||||
export function createFastDomNode<T extends HTMLElement>(domNode: T): FastDomNode<T> {
|
||||
if (useWebKitFastDomNode) {
|
||||
return new WebKitFastDomNode(domNode);
|
||||
} else {
|
||||
|
||||
@@ -48,12 +48,12 @@ export abstract class AbstractScrollbar extends Widget {
|
||||
protected _host: ScrollbarHost;
|
||||
protected _scrollable: Scrollable;
|
||||
private _lazyRender: boolean;
|
||||
private _scrollbarState: ScrollbarState;
|
||||
protected _scrollbarState: ScrollbarState;
|
||||
private _visibilityController: ScrollbarVisibilityController;
|
||||
private _mouseMoveMonitor: GlobalMouseMoveMonitor<IStandardMouseMoveEventData>;
|
||||
|
||||
public domNode: FastDomNode;
|
||||
public slider: FastDomNode;
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
public slider: FastDomNode<HTMLElement>;
|
||||
|
||||
protected _shouldRender: boolean;
|
||||
|
||||
|
||||
@@ -37,9 +37,9 @@ export class ScrollableElement extends Widget {
|
||||
private _horizontalScrollbar: HorizontalScrollbar;
|
||||
private _domNode: HTMLElement;
|
||||
|
||||
private _leftShadowDomNode: FastDomNode;
|
||||
private _topShadowDomNode: FastDomNode;
|
||||
private _topLeftShadowDomNode: FastDomNode;
|
||||
private _leftShadowDomNode: FastDomNode<HTMLElement>;
|
||||
private _topShadowDomNode: FastDomNode<HTMLElement>;
|
||||
private _topLeftShadowDomNode: FastDomNode<HTMLElement>;
|
||||
|
||||
private _listenOnDomNode: HTMLElement;
|
||||
|
||||
@@ -145,6 +145,10 @@ export class ScrollableElement extends Widget {
|
||||
this._verticalScrollbar.delegateMouseDown(browserEvent);
|
||||
}
|
||||
|
||||
public getVerticalSliderVerticalCenter(): number {
|
||||
return this._verticalScrollbar.getVerticalSliderVerticalCenter();
|
||||
}
|
||||
|
||||
public updateState(newState: INewScrollState): void {
|
||||
this._scrollable.updateState(newState);
|
||||
}
|
||||
@@ -365,7 +369,9 @@ export class ScrollableElement extends Widget {
|
||||
}
|
||||
|
||||
private _scheduleHide(): void {
|
||||
this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);
|
||||
if (!this._mouseIsOver && !this._isDragging) {
|
||||
this._hideTimeout.cancelAndSet(() => this._hide(), HIDE_TIMEOUT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +171,10 @@ export class ScrollbarState {
|
||||
return this._computedSliderPosition;
|
||||
}
|
||||
|
||||
public getSliderCenter(): number {
|
||||
return (this._computedSliderPosition + this._computedSliderSize / 2);
|
||||
}
|
||||
|
||||
public convertSliderPositionToScrollPosition(desiredSliderPosition: number): number {
|
||||
return desiredSliderPosition / this._computedRatio;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ export class ScrollbarVisibilityController extends Disposable {
|
||||
private _visibility: ScrollbarVisibility;
|
||||
private _visibleClassName: string;
|
||||
private _invisibleClassName: string;
|
||||
private _domNode: FastDomNode;
|
||||
private _domNode: FastDomNode<HTMLElement>;
|
||||
private _shouldBeVisible: boolean;
|
||||
private _isNeeded: boolean;
|
||||
private _isVisible: boolean;
|
||||
@@ -59,7 +59,7 @@ export class ScrollbarVisibilityController extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
public setDomNode(domNode: FastDomNode): void {
|
||||
public setDomNode(domNode: FastDomNode<HTMLElement>): void {
|
||||
this._domNode = domNode;
|
||||
this._domNode.setClassName(this._invisibleClassName);
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ export class VerticalScrollbar extends AbstractScrollbar {
|
||||
this._createSlider(0, Math.floor((options.verticalScrollbarSize - options.verticalSliderSize) / 2), options.verticalSliderSize, null);
|
||||
}
|
||||
|
||||
public getVerticalSliderVerticalCenter(): number {
|
||||
return this._scrollbarState.getSliderCenter();
|
||||
}
|
||||
|
||||
protected _updateSlider(sliderSize: number, sliderPosition: number): void {
|
||||
this.slider.setHeight(sliderSize);
|
||||
if (this._canUseTranslate3d) {
|
||||
|
||||
+333
-168
@@ -3,27 +3,161 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as Object from 'vs/base/common/objects';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
|
||||
export interface RGBA { r: number; g: number; b: number; a: number; }
|
||||
export interface HSLA { h: number; s: number; l: number; a: number; }
|
||||
export class RGBA {
|
||||
_rgbaBrand: void;
|
||||
|
||||
/**
|
||||
* Red: integer in [0-255]
|
||||
*/
|
||||
public readonly r: number;
|
||||
/**
|
||||
* Green: integer in [0-255]
|
||||
*/
|
||||
public readonly g: number;
|
||||
/**
|
||||
* Blue: integer in [0-255]
|
||||
*/
|
||||
public readonly b: number;
|
||||
/**
|
||||
* Alpha: integer in [0-255]
|
||||
*/
|
||||
public readonly a: number;
|
||||
|
||||
constructor(r: number, g: number, b: number, a: number) {
|
||||
this.r = RGBA._clampInt_0_255(r);
|
||||
this.g = RGBA._clampInt_0_255(g);
|
||||
this.b = RGBA._clampInt_0_255(b);
|
||||
this.a = RGBA._clampInt_0_255(a);
|
||||
}
|
||||
|
||||
public static equals(a: RGBA, b: RGBA): boolean {
|
||||
return (
|
||||
a.r === b.r
|
||||
&& a.g === b.g
|
||||
&& a.b === b.b
|
||||
&& a.a === b.a
|
||||
);
|
||||
}
|
||||
|
||||
private static _clampInt_0_255(c: number): number {
|
||||
if (c < 0) {
|
||||
return 0;
|
||||
}
|
||||
if (c > 255) {
|
||||
return 255;
|
||||
}
|
||||
return c | 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* http://en.wikipedia.org/wiki/HSL_color_space
|
||||
*/
|
||||
export class HSLA {
|
||||
_hslaBrand: void;
|
||||
|
||||
/**
|
||||
* Hue: float in [0, 360]
|
||||
*/
|
||||
public readonly h: number;
|
||||
/**
|
||||
* Saturation: float in [0, 1]
|
||||
*/
|
||||
public readonly s: number;
|
||||
/**
|
||||
* Luminosity: float in [0, 1]
|
||||
*/
|
||||
public readonly l: number;
|
||||
/**
|
||||
* Alpha: float in [0, 1]
|
||||
*/
|
||||
public readonly a: number;
|
||||
|
||||
constructor(h: number, s: number, l: number, a: number) {
|
||||
this.h = HSLA._clampFloat_0_360(h);
|
||||
this.s = HSLA._clampFloat_0_1(s);
|
||||
this.l = HSLA._clampFloat_0_1(l);
|
||||
this.a = HSLA._clampFloat_0_1(a);
|
||||
}
|
||||
|
||||
private static _clampFloat_0_360(hue: number): number {
|
||||
if (hue < 0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (hue > 360) {
|
||||
return 360.0;
|
||||
}
|
||||
return hue;
|
||||
}
|
||||
|
||||
private static _clampFloat_0_1(n: number): number {
|
||||
if (n < 0) {
|
||||
return 0.0;
|
||||
}
|
||||
if (n > 1) {
|
||||
return 1.0;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an Hex color value to RGB.
|
||||
* returns r, g, and b are contained in the set [0, 255]
|
||||
* @param hex string (#RRGGBB or #RRGGBBAA).
|
||||
*/
|
||||
function hex2rgba(hex: string): RGBA {
|
||||
function parseHex(str: string) {
|
||||
return parseInt('0x' + str);
|
||||
if (!hex) {
|
||||
// Invalid color
|
||||
return new RGBA(255, 0, 0, 255);
|
||||
}
|
||||
if (hex.charAt(0) === '#' && hex.length >= 7) {
|
||||
let r = parseHex(hex.substr(1, 2));
|
||||
let g = parseHex(hex.substr(3, 2));
|
||||
let b = parseHex(hex.substr(5, 2));
|
||||
let a = hex.length === 9 ? parseHex(hex.substr(7, 2)) / 0xff : 1;
|
||||
return { r, g, b, a };
|
||||
if (hex.length === 7 && hex.charCodeAt(0) === CharCode.Hash) {
|
||||
// #RRGGBB format
|
||||
const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
|
||||
const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
|
||||
const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
|
||||
return new RGBA(r, g, b, 255);
|
||||
}
|
||||
return { r: 255, g: 0, b: 0, a: 1 };
|
||||
if (hex.length === 9 && hex.charCodeAt(0) === CharCode.Hash) {
|
||||
// #RRGGBBAA format
|
||||
const r = 16 * _parseHexDigit(hex.charCodeAt(1)) + _parseHexDigit(hex.charCodeAt(2));
|
||||
const g = 16 * _parseHexDigit(hex.charCodeAt(3)) + _parseHexDigit(hex.charCodeAt(4));
|
||||
const b = 16 * _parseHexDigit(hex.charCodeAt(5)) + _parseHexDigit(hex.charCodeAt(6));
|
||||
const a = 16 * _parseHexDigit(hex.charCodeAt(7)) + _parseHexDigit(hex.charCodeAt(8));
|
||||
return new RGBA(r, g, b, a);
|
||||
}
|
||||
// Invalid color
|
||||
return new RGBA(255, 0, 0, 255);
|
||||
}
|
||||
|
||||
function _parseHexDigit(charCode: CharCode): number {
|
||||
switch (charCode) {
|
||||
case CharCode.Digit0: return 0;
|
||||
case CharCode.Digit1: return 1;
|
||||
case CharCode.Digit2: return 2;
|
||||
case CharCode.Digit3: return 3;
|
||||
case CharCode.Digit4: return 4;
|
||||
case CharCode.Digit5: return 5;
|
||||
case CharCode.Digit6: return 6;
|
||||
case CharCode.Digit7: return 7;
|
||||
case CharCode.Digit8: return 8;
|
||||
case CharCode.Digit9: return 9;
|
||||
case CharCode.a: return 10;
|
||||
case CharCode.A: return 10;
|
||||
case CharCode.b: return 11;
|
||||
case CharCode.B: return 11;
|
||||
case CharCode.c: return 12;
|
||||
case CharCode.C: return 12;
|
||||
case CharCode.d: return 13;
|
||||
case CharCode.D: return 13;
|
||||
case CharCode.e: return 14;
|
||||
case CharCode.E: return 14;
|
||||
case CharCode.f: return 15;
|
||||
case CharCode.F: return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,13 +167,17 @@ function hex2rgba(hex: string): RGBA {
|
||||
* returns h in the set [0, 360], s, and l in the set [0, 1].
|
||||
*/
|
||||
function rgba2hsla(rgba: RGBA): HSLA {
|
||||
let r = rgba.r / 255;
|
||||
let g = rgba.g / 255;
|
||||
let b = rgba.b / 255;
|
||||
let a = rgba.a === void 0 ? rgba.a : 1;
|
||||
const r = rgba.r / 255;
|
||||
const g = rgba.g / 255;
|
||||
const b = rgba.b / 255;
|
||||
const a = rgba.a / 255;
|
||||
|
||||
let max = Math.max(r, g, b), min = Math.min(r, g, b);
|
||||
let h = 0, s = 0, l = Math.round(((min + max) / 2) * 1000) / 1000, chroma = max - min;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h = 0;
|
||||
let s = 0;
|
||||
const l = Math.round(((min + max) / 2) * 1000) / 1000;
|
||||
const chroma = max - min;
|
||||
|
||||
if (chroma > 0) {
|
||||
s = Math.min(Math.round((l <= 0.5 ? chroma / (2 * l) : chroma / (2 - (2 * l))) * 1000) / 1000, 1);
|
||||
@@ -51,7 +189,7 @@ function rgba2hsla(rgba: RGBA): HSLA {
|
||||
h *= 60;
|
||||
h = Math.round(h);
|
||||
}
|
||||
return { h, s, l, a };
|
||||
return new HSLA(h, s, l, a);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,170 +199,53 @@ function rgba2hsla(rgba: RGBA): HSLA {
|
||||
* returns r, g, and b in the set [0, 255].
|
||||
*/
|
||||
function hsla2rgba(hsla: HSLA): RGBA {
|
||||
let h = hsla.h / 360;
|
||||
let s = Math.min(hsla.s, 1);
|
||||
let l = Math.min(hsla.l, 1);
|
||||
let a = hsla.a === void 0 ? hsla.a : 1;
|
||||
const h = hsla.h / 360;
|
||||
const s = Math.min(hsla.s, 1);
|
||||
const l = Math.min(hsla.l, 1);
|
||||
const a = hsla.a;
|
||||
let r: number, g: number, b: number;
|
||||
|
||||
if (s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
} else {
|
||||
let hue2rgb = function hue2rgb(p: number, q: number, t: number) {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
}
|
||||
if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
let p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1 / 3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1 / 3);
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
const p = 2 * l - q;
|
||||
r = _hue2rgb(p, q, h + 1 / 3);
|
||||
g = _hue2rgb(p, q, h);
|
||||
b = _hue2rgb(p, q, h - 1 / 3);
|
||||
}
|
||||
|
||||
return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), a };
|
||||
return new RGBA(Math.round(r * 255), Math.round(g * 255), Math.round(b * 255), Math.round(a * 255));
|
||||
}
|
||||
|
||||
export function hexToCSS(hex: string) {
|
||||
if (hex.length === 9) {
|
||||
return toCSSColor(hex2rgba(hex));
|
||||
function _hue2rgb(p: number, q: number, t: number) {
|
||||
if (t < 0) {
|
||||
t += 1;
|
||||
}
|
||||
return hex;
|
||||
}
|
||||
|
||||
function toCSSColor(rgba: RGBA): string {
|
||||
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${+rgba.a.toFixed(2)})`;
|
||||
if (t > 1) {
|
||||
t -= 1;
|
||||
}
|
||||
if (t < 1 / 6) {
|
||||
return p + (q - p) * 6 * t;
|
||||
}
|
||||
if (t < 1 / 2) {
|
||||
return q;
|
||||
}
|
||||
if (t < 2 / 3) {
|
||||
return p + (q - p) * (2 / 3 - t) * 6;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
export class Color {
|
||||
|
||||
private rgba: RGBA;
|
||||
private hsla: HSLA;
|
||||
private str: string;
|
||||
|
||||
constructor(arg: string | RGBA) {
|
||||
this.rgba = typeof arg === 'string' ? hex2rgba(arg) : <RGBA>arg;
|
||||
this.str = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
* Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
|
||||
*/
|
||||
public getLuminosity(): number {
|
||||
let luminosityFor = function (color: number): number {
|
||||
let c = color / 255;
|
||||
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
|
||||
};
|
||||
let R = luminosityFor(this.rgba.r);
|
||||
let G = luminosityFor(this.rgba.g);
|
||||
let B = luminosityFor(this.rgba.b);
|
||||
let luminosity = 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
||||
return Math.round(luminosity * 10000) / 10000;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
||||
* Returns the contrast ration number in the set [1, 21].
|
||||
*/
|
||||
public getContrast(another: Color): number {
|
||||
let lum1 = this.getLuminosity();
|
||||
let lum2 = another.getLuminosity();
|
||||
return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://24ways.org/2010/calculating-color-contrast
|
||||
* Return 'true' if darker color otherwise 'false'
|
||||
*/
|
||||
public isDarker(): boolean {
|
||||
var yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
|
||||
return yiq < 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://24ways.org/2010/calculating-color-contrast
|
||||
* Return 'true' if lighter color otherwise 'false'
|
||||
*/
|
||||
public isLighter(): boolean {
|
||||
var yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
|
||||
return yiq >= 128;
|
||||
}
|
||||
|
||||
public isLighterThan(another: Color): boolean {
|
||||
let lum1 = this.getLuminosity();
|
||||
let lum2 = another.getLuminosity();
|
||||
return lum1 > lum2;
|
||||
}
|
||||
|
||||
public isDarkerThan(another: Color): boolean {
|
||||
let lum1 = this.getLuminosity();
|
||||
let lum2 = another.getLuminosity();
|
||||
return lum1 < lum2;
|
||||
}
|
||||
|
||||
public lighten(factor: number): Color {
|
||||
let hsl = this.toHSLA();
|
||||
hsl.l += hsl.l * factor;
|
||||
return new Color(hsla2rgba(hsl));
|
||||
}
|
||||
|
||||
public darken(factor: number): Color {
|
||||
let hsl = this.toHSLA();
|
||||
hsl.l -= hsl.l * factor;
|
||||
return new Color(hsla2rgba(hsl));
|
||||
}
|
||||
|
||||
public transparent(factor: number): Color {
|
||||
let p = this.rgba;
|
||||
return new Color({ r: p.r, g: p.g, b: p.b, a: p.a * factor });
|
||||
}
|
||||
|
||||
public opposite(): Color {
|
||||
return new Color({
|
||||
r: 255 - this.rgba.r,
|
||||
g: 255 - this.rgba.g,
|
||||
b: 255 - this.rgba.b,
|
||||
a: this.rgba.a
|
||||
});
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
if (!this.str) {
|
||||
this.str = toCSSColor(this.rgba);
|
||||
}
|
||||
return this.str;
|
||||
}
|
||||
|
||||
public toHSLA(): HSLA {
|
||||
if (!this.hsla) {
|
||||
this.hsla = rgba2hsla(this.rgba);
|
||||
}
|
||||
return Object.clone(this.hsla);
|
||||
}
|
||||
|
||||
public toRGBA(): RGBA {
|
||||
return Object.clone(this.rgba);
|
||||
}
|
||||
|
||||
public static fromRGBA(rgba: RGBA): Color {
|
||||
return new Color(rgba);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a color from a hex string (#RRGGBB or #RRGGBBAA).
|
||||
*/
|
||||
public static fromHex(hex: string): Color {
|
||||
return new Color(hex);
|
||||
}
|
||||
@@ -233,12 +254,155 @@ export class Color {
|
||||
return new Color(hsla2rgba(hsla));
|
||||
}
|
||||
|
||||
private readonly rgba: RGBA;
|
||||
private hsla: HSLA;
|
||||
|
||||
private constructor(arg: string | RGBA) {
|
||||
if (arg instanceof RGBA) {
|
||||
this.rgba = arg;
|
||||
} else {
|
||||
this.rgba = hex2rgba(arg);
|
||||
}
|
||||
this.hsla = null;
|
||||
}
|
||||
|
||||
public equals(other: Color): boolean {
|
||||
if (!other) {
|
||||
return false;
|
||||
}
|
||||
return RGBA.equals(this.rgba, other.rgba);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
||||
* Returns the number in the set [0, 1]. O => Darkest Black. 1 => Lightest white.
|
||||
*/
|
||||
public getLuminosity(): number {
|
||||
const R = Color._luminosityFor(this.rgba.r);
|
||||
const G = Color._luminosityFor(this.rgba.g);
|
||||
const B = Color._luminosityFor(this.rgba.b);
|
||||
const luminosity = 0.2126 * R + 0.7152 * G + 0.0722 * B;
|
||||
return Math.round(luminosity * 10000) / 10000;
|
||||
}
|
||||
|
||||
private static _luminosityFor(color: number): number {
|
||||
const c = color / 255;
|
||||
return (c <= 0.03928) ? c / 12.92 : Math.pow(((c + 0.055) / 1.055), 2.4);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
||||
* Returns the contrast ration number in the set [1, 21].
|
||||
*/
|
||||
public getContrast(another: Color): number {
|
||||
const lum1 = this.getLuminosity();
|
||||
const lum2 = another.getLuminosity();
|
||||
return lum1 > lum2 ? (lum1 + 0.05) / (lum2 + 0.05) : (lum2 + 0.05) / (lum1 + 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* http://24ways.org/2010/calculating-color-contrast
|
||||
* Return 'true' if darker color otherwise 'false'
|
||||
*/
|
||||
public isDarker(): boolean {
|
||||
const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
|
||||
return yiq < 128;
|
||||
}
|
||||
|
||||
/**
|
||||
* http://24ways.org/2010/calculating-color-contrast
|
||||
* Return 'true' if lighter color otherwise 'false'
|
||||
*/
|
||||
public isLighter(): boolean {
|
||||
const yiq = (this.rgba.r * 299 + this.rgba.g * 587 + this.rgba.b * 114) / 1000;
|
||||
return yiq >= 128;
|
||||
}
|
||||
|
||||
public isLighterThan(another: Color): boolean {
|
||||
const lum1 = this.getLuminosity();
|
||||
const lum2 = another.getLuminosity();
|
||||
return lum1 > lum2;
|
||||
}
|
||||
|
||||
public isDarkerThan(another: Color): boolean {
|
||||
const lum1 = this.getLuminosity();
|
||||
const lum2 = another.getLuminosity();
|
||||
return lum1 < lum2;
|
||||
}
|
||||
|
||||
public lighten(factor: number): Color {
|
||||
const hsl = this.toHSLA();
|
||||
const result = new HSLA(hsl.h, hsl.s, hsl.l + hsl.l * factor, hsl.a);
|
||||
return new Color(hsla2rgba(result));
|
||||
}
|
||||
|
||||
public darken(factor: number): Color {
|
||||
const hsl = this.toHSLA();
|
||||
const result = new HSLA(hsl.h, hsl.s, hsl.l - hsl.l * factor, hsl.a);
|
||||
return new Color(hsla2rgba(result));
|
||||
}
|
||||
|
||||
public transparent(factor: number): Color {
|
||||
const p = this.rgba;
|
||||
return new Color(new RGBA(p.r, p.g, p.b, Math.round(p.a * factor)));
|
||||
}
|
||||
|
||||
public opposite(): Color {
|
||||
return new Color(new RGBA(
|
||||
255 - this.rgba.r,
|
||||
255 - this.rgba.g,
|
||||
255 - this.rgba.b,
|
||||
this.rgba.a
|
||||
));
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
const rgba = this.rgba;
|
||||
return `rgba(${rgba.r}, ${rgba.g}, ${rgba.b}, ${+(rgba.a / 255).toFixed(2)})`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prins the color as #RRGGBB
|
||||
*/
|
||||
public toRGBHex(): string {
|
||||
const rgba = this.rgba;
|
||||
return `#${Color._toTwoDigitHex(rgba.r)}${Color._toTwoDigitHex(rgba.g)}${Color._toTwoDigitHex(rgba.b)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prins the color as #RRGGBBAA
|
||||
*/
|
||||
public toRGBAHex(): string {
|
||||
const rgba = this.rgba;
|
||||
return `#${Color._toTwoDigitHex(rgba.r)}${Color._toTwoDigitHex(rgba.g)}${Color._toTwoDigitHex(rgba.b)}${Color._toTwoDigitHex(rgba.a)}`;
|
||||
}
|
||||
|
||||
private static _toTwoDigitHex(n: number): string {
|
||||
let r = n.toString(16);
|
||||
if (r.length !== 2) {
|
||||
return '0' + r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public toHSLA(): HSLA {
|
||||
if (this.hsla === null) {
|
||||
this.hsla = rgba2hsla(this.rgba);
|
||||
}
|
||||
return this.hsla;
|
||||
}
|
||||
|
||||
public toRGBA(): RGBA {
|
||||
return this.rgba;
|
||||
}
|
||||
|
||||
public static getLighterColor(of: Color, relative: Color, factor?: number): Color {
|
||||
if (of.isLighterThan(relative)) {
|
||||
return of;
|
||||
}
|
||||
factor = factor ? factor : 0.5;
|
||||
let lum1 = of.getLuminosity(), lum2 = relative.getLuminosity();
|
||||
const lum1 = of.getLuminosity();
|
||||
const lum2 = relative.getLuminosity();
|
||||
factor = factor * (lum2 - lum1) / lum2;
|
||||
return of.lighten(factor);
|
||||
}
|
||||
@@ -248,8 +412,9 @@ export class Color {
|
||||
return of;
|
||||
}
|
||||
factor = factor ? factor : 0.5;
|
||||
let lum1 = of.getLuminosity(), lum2 = relative.getLuminosity();
|
||||
const lum1 = of.getLuminosity();
|
||||
const lum2 = relative.getLuminosity();
|
||||
factor = factor * (lum1 - lum2) / lum1;
|
||||
return of.darken(factor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,82 +5,112 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import * as assert from 'assert';
|
||||
import { Color, RGBA, HSLA } from 'vs/base/common/color';
|
||||
|
||||
suite('Color', () => {
|
||||
|
||||
test('rgba2hsla', function () {
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 0, l: 1, a: 1 }, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0, 1), Color.fromRGBA(new RGBA(0, 0, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 1, 1), Color.fromRGBA(new RGBA(255, 255, 255, 255)).toHSLA());
|
||||
|
||||
assert.deepEqual({ h: 0, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 120, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 240, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 1, 0.5, 1), Color.fromRGBA(new RGBA(255, 0, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(120, 1, 0.5, 1), Color.fromRGBA(new RGBA(0, 255, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(240, 1, 0.5, 1), Color.fromRGBA(new RGBA(0, 0, 255, 255)).toHSLA());
|
||||
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 180, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 300, s: 1, l: 0.5, a: 1 }, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.5, 1), Color.fromRGBA(new RGBA(255, 255, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(180, 1, 0.5, 1), Color.fromRGBA(new RGBA(0, 255, 255, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(300, 1, 0.5, 1), Color.fromRGBA(new RGBA(255, 0, 255, 255)).toHSLA());
|
||||
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0.753, a: 1 }, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0.753, 1), Color.fromRGBA(new RGBA(192, 192, 192, 255)).toHSLA());
|
||||
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0.502, a: 1 }, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 120, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 300, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 180, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).toHSLA());
|
||||
assert.deepEqual({ h: 240, s: 1, l: 0.251, a: 1 }, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0.502, 1), Color.fromRGBA(new RGBA(128, 128, 128, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 1, 0.251, 1), Color.fromRGBA(new RGBA(128, 0, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.251, 1), Color.fromRGBA(new RGBA(128, 128, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(120, 1, 0.251, 1), Color.fromRGBA(new RGBA(0, 128, 0, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(300, 1, 0.251, 1), Color.fromRGBA(new RGBA(128, 0, 128, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(180, 1, 0.251, 1), Color.fromRGBA(new RGBA(0, 128, 128, 255)).toHSLA());
|
||||
assert.deepEqual(new HSLA(240, 1, 0.251, 1), Color.fromRGBA(new RGBA(0, 0, 128, 255)).toHSLA());
|
||||
});
|
||||
|
||||
test('hsla2rgba', function () {
|
||||
assert.deepEqual({ r: 0, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 255, g: 255, b: 255, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 1, a: 1 }).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 0, 255), Color.fromHSLA(new HSLA(0, 0, 0, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 255, 255, 255), Color.fromHSLA(new HSLA(0, 0, 1, 1)).toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 255, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 255, b: 0, a: 1 }, Color.fromHSLA({ h: 120, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 0, b: 255, a: 1 }, Color.fromHSLA({ h: 240, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 0, 0, 255), Color.fromHSLA(new HSLA(0, 1, 0.5, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 255, 0, 255), Color.fromHSLA(new HSLA(120, 1, 0.5, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 255, 255), Color.fromHSLA(new HSLA(240, 1, 0.5, 1)).toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 255, g: 255, b: 0, a: 1 }, Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 255, b: 255, a: 1 }, Color.fromHSLA({ h: 180, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 255, g: 0, b: 255, a: 1 }, Color.fromHSLA({ h: 300, s: 1, l: 0.5, a: 1 }).toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 255, 0, 255), Color.fromHSLA(new HSLA(60, 1, 0.5, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 255, 255, 255), Color.fromHSLA(new HSLA(180, 1, 0.5, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 0, 255, 255), Color.fromHSLA(new HSLA(300, 1, 0.5, 1)).toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 192, g: 192, b: 192, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 }).toRGBA());
|
||||
assert.deepEqual(new RGBA(192, 192, 192, 255), Color.fromHSLA(new HSLA(0, 0, 0.753, 1)).toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 128, g: 128, b: 128, a: 1 }, Color.fromHSLA({ h: 0, s: 0, l: 0.502, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 0, b: 0, a: 1 }, Color.fromHSLA({ h: 0, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 128, b: 0, a: 1 }, Color.fromHSLA({ h: 60, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 128, b: 0, a: 1 }, Color.fromHSLA({ h: 120, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 0, b: 128, a: 1 }, Color.fromHSLA({ h: 300, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 128, b: 128, a: 1 }, Color.fromHSLA({ h: 180, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 0, b: 128, a: 1 }, Color.fromHSLA({ h: 240, s: 1, l: 0.251, a: 1 }).toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 128, 128, 255), Color.fromHSLA(new HSLA(0, 0, 0.502, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 0, 0, 255), Color.fromHSLA(new HSLA(0, 1, 0.251, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 128, 0, 255), Color.fromHSLA(new HSLA(60, 1, 0.251, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 128, 0, 255), Color.fromHSLA(new HSLA(120, 1, 0.251, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 0, 128, 255), Color.fromHSLA(new HSLA(300, 1, 0.251, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 128, 128, 255), Color.fromHSLA(new HSLA(180, 1, 0.251, 1)).toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 128, 255), Color.fromHSLA(new HSLA(240, 1, 0.251, 1)).toRGBA());
|
||||
});
|
||||
|
||||
test('hex2rgba', function () {
|
||||
assert.deepEqual({ r: 0, g: 0, b: 0, a: 1 }, Color.fromHex('#000000').toRGBA());
|
||||
assert.deepEqual({ r: 255, g: 255, b: 255, a: 1 }, Color.fromHex('#FFFFFF').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 0, 255), Color.fromHex('#000000').toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 255, 255, 255), Color.fromHex('#FFFFFF').toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 255, g: 0, b: 0, a: 1 }, Color.fromHex('#FF0000').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 255, b: 0, a: 1 }, Color.fromHex('#00FF00').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 0, b: 255, a: 1 }, Color.fromHex('#0000FF').toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 0, 0, 255), Color.fromHex('#FF0000').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 255, 0, 255), Color.fromHex('#00FF00').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 255, 255), Color.fromHex('#0000FF').toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 255, g: 255, b: 0, a: 1 }, Color.fromHex('#FFFF00').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 255, b: 255, a: 1 }, Color.fromHex('#00FFFF').toRGBA());
|
||||
assert.deepEqual({ r: 255, g: 0, b: 255, a: 1 }, Color.fromHex('#FF00FF').toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 255, 0, 255), Color.fromHex('#FFFF00').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 255, 255, 255), Color.fromHex('#00FFFF').toRGBA());
|
||||
assert.deepEqual(new RGBA(255, 0, 255, 255), Color.fromHex('#FF00FF').toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 192, g: 192, b: 192, a: 1 }, Color.fromHex('#C0C0C0').toRGBA());
|
||||
assert.deepEqual(new RGBA(192, 192, 192, 255), Color.fromHex('#C0C0C0').toRGBA());
|
||||
|
||||
assert.deepEqual({ r: 128, g: 128, b: 128, a: 1 }, Color.fromHex('#808080').toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 0, b: 0, a: 1 }, Color.fromHex('#800000').toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 128, b: 0, a: 1 }, Color.fromHex('#808000').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 128, b: 0, a: 1 }, Color.fromHex('#008000').toRGBA());
|
||||
assert.deepEqual({ r: 128, g: 0, b: 128, a: 1 }, Color.fromHex('#800080').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 128, b: 128, a: 1 }, Color.fromHex('#008080').toRGBA());
|
||||
assert.deepEqual({ r: 0, g: 0, b: 128, a: 1 }, Color.fromHex('#000080').toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 128, 128, 255), Color.fromHex('#808080').toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 0, 0, 255), Color.fromHex('#800000').toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 128, 0, 255), Color.fromHex('#808000').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 128, 0, 255), Color.fromHex('#008000').toRGBA());
|
||||
assert.deepEqual(new RGBA(128, 0, 128, 255), Color.fromHex('#800080').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 128, 128, 255), Color.fromHex('#008080').toRGBA());
|
||||
assert.deepEqual(new RGBA(0, 0, 128, 255), Color.fromHex('#000080').toRGBA());
|
||||
|
||||
function assertParseColor(input: string, expected: RGBA): void {
|
||||
let actual = Color.fromHex(input).toRGBA();
|
||||
assert.deepEqual(actual, expected, input);
|
||||
}
|
||||
|
||||
// invalid
|
||||
assertParseColor(null, new RGBA(255, 0, 0, 255));
|
||||
assertParseColor('', new RGBA(255, 0, 0, 255));
|
||||
assertParseColor('#', new RGBA(255, 0, 0, 255));
|
||||
assertParseColor('#0102030', new RGBA(255, 0, 0, 255));
|
||||
|
||||
// somewhat valid
|
||||
assertParseColor('#FFFFG0', new RGBA(255, 255, 0, 255));
|
||||
assertParseColor('#FFFFg0', new RGBA(255, 255, 0, 255));
|
||||
assertParseColor('#-FFF00', new RGBA(15, 255, 0, 255));
|
||||
|
||||
// valid
|
||||
assertParseColor('#000000', new RGBA(0, 0, 0, 255));
|
||||
assertParseColor('#010203', new RGBA(1, 2, 3, 255));
|
||||
assertParseColor('#040506', new RGBA(4, 5, 6, 255));
|
||||
assertParseColor('#070809', new RGBA(7, 8, 9, 255));
|
||||
assertParseColor('#0a0A0a', new RGBA(10, 10, 10, 255));
|
||||
assertParseColor('#0b0B0b', new RGBA(11, 11, 11, 255));
|
||||
assertParseColor('#0c0C0c', new RGBA(12, 12, 12, 255));
|
||||
assertParseColor('#0d0D0d', new RGBA(13, 13, 13, 255));
|
||||
assertParseColor('#0e0E0e', new RGBA(14, 14, 14, 255));
|
||||
assertParseColor('#0f0F0f', new RGBA(15, 15, 15, 255));
|
||||
assertParseColor('#a0A0a0', new RGBA(160, 160, 160, 255));
|
||||
assertParseColor('#FFFFFF', new RGBA(255, 255, 255, 255));
|
||||
});
|
||||
|
||||
test('isLighterColor', function () {
|
||||
let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 });
|
||||
let color1 = Color.fromHSLA(new HSLA(60, 1, 0.5, 1)), color2 = Color.fromHSLA(new HSLA(0, 0, 0.753, 1));
|
||||
|
||||
assert.ok(color1.isLighterThan(color2));
|
||||
|
||||
@@ -89,80 +119,80 @@ suite('Color', () => {
|
||||
});
|
||||
|
||||
test('getLighterColor', function () {
|
||||
let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 });
|
||||
let color1 = Color.fromHSLA(new HSLA(60, 1, 0.5, 1)), color2 = Color.fromHSLA(new HSLA(0, 0, 0.753, 1));
|
||||
|
||||
assert.deepEqual(color1.toHSLA(), Color.getLighterColor(color1, color2).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0.914, a: 1 }, Color.getLighterColor(color2, color1).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0.851, a: 1 }, Color.getLighterColor(color2, color1, 0.3).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 0, l: 0.98, a: 1 }, Color.getLighterColor(color2, color1, 0.7).toHSLA());
|
||||
assert.deepEqual({ h: 0, s: 0, l: 1, a: 1 }, Color.getLighterColor(color2, color1, 1).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0.914, 1), Color.getLighterColor(color2, color1).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0.851, 1), Color.getLighterColor(color2, color1, 0.3).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 0.98, 1), Color.getLighterColor(color2, color1, 0.7).toHSLA());
|
||||
assert.deepEqual(new HSLA(0, 0, 1, 1), Color.getLighterColor(color2, color1, 1).toHSLA());
|
||||
|
||||
});
|
||||
|
||||
test('isDarkerColor', function () {
|
||||
let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 });
|
||||
let color1 = Color.fromHSLA(new HSLA(60, 1, 0.5, 1)), color2 = Color.fromHSLA(new HSLA(0, 0, 0.753, 1));
|
||||
|
||||
assert.ok(color2.isDarkerThan(color1));
|
||||
|
||||
});
|
||||
|
||||
test('getDarkerColor', function () {
|
||||
let color1 = Color.fromHSLA({ h: 60, s: 1, l: 0.5, a: 1 }), color2 = Color.fromHSLA({ h: 0, s: 0, l: 0.753, a: 1 });
|
||||
let color1 = Color.fromHSLA(new HSLA(60, 1, 0.5, 1)), color2 = Color.fromHSLA(new HSLA(0, 0, 0.753, 1));
|
||||
|
||||
assert.deepEqual(color2.toHSLA(), Color.getDarkerColor(color2, color1).toHSLA());
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.392, a: 1 }, Color.getDarkerColor(color1, color2).toHSLA());
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.435, a: 1 }, Color.getDarkerColor(color1, color2, 0.3).toHSLA());
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.349, a: 1 }, Color.getDarkerColor(color1, color2, 0.7).toHSLA());
|
||||
assert.deepEqual({ h: 60, s: 1, l: 0.284, a: 1 }, Color.getDarkerColor(color1, color2, 1).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.392, 1), Color.getDarkerColor(color1, color2).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.435, 1), Color.getDarkerColor(color1, color2, 0.3).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.349, 1), Color.getDarkerColor(color1, color2, 0.7).toHSLA());
|
||||
assert.deepEqual(new HSLA(60, 1, 0.284, 1), Color.getDarkerColor(color1, color2, 1).toHSLA());
|
||||
|
||||
// Abyss theme
|
||||
assert.deepEqual({ h: 355, s: 0.874, l: 0.157, a: 1 }, Color.getDarkerColor(Color.fromHex('#770811'), Color.fromHex('#000c18'), 0.4).toHSLA());
|
||||
assert.deepEqual(new HSLA(355, 0.874, 0.157, 1), Color.getDarkerColor(Color.fromHex('#770811'), Color.fromHex('#000c18'), 0.4).toHSLA());
|
||||
});
|
||||
|
||||
test('luminosity', function () {
|
||||
assert.deepEqual(0, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(1, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0, Color.fromRGBA(new RGBA(0, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(1, Color.fromRGBA(new RGBA(255, 255, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.2126, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.7152, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0722, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2126, Color.fromRGBA(new RGBA(255, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.7152, Color.fromRGBA(new RGBA(0, 255, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0722, Color.fromRGBA(new RGBA(0, 0, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.9278, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.7874, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2848, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.9278, Color.fromRGBA(new RGBA(255, 255, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.7874, Color.fromRGBA(new RGBA(0, 255, 255, 255)).getLuminosity());
|
||||
assert.deepEqual(0.2848, Color.fromRGBA(new RGBA(255, 0, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.5271, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.5271, Color.fromRGBA(new RGBA(192, 192, 192, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.2159, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0459, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2003, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.1544, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0615, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.17, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0156, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2159, Color.fromRGBA(new RGBA(128, 128, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0459, Color.fromRGBA(new RGBA(128, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.2003, Color.fromRGBA(new RGBA(128, 128, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.1544, Color.fromRGBA(new RGBA(0, 128, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0615, Color.fromRGBA(new RGBA(128, 0, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.17, Color.fromRGBA(new RGBA(0, 128, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0156, Color.fromRGBA(new RGBA(0, 0, 128, 255)).getLuminosity());
|
||||
});
|
||||
|
||||
test('contrast', function () {
|
||||
assert.deepEqual(0, Color.fromRGBA({ r: 0, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(1, Color.fromRGBA({ r: 255, g: 255, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0, Color.fromRGBA(new RGBA(0, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(1, Color.fromRGBA(new RGBA(255, 255, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.2126, Color.fromRGBA({ r: 255, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.7152, Color.fromRGBA({ r: 0, g: 255, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0722, Color.fromRGBA({ r: 0, g: 0, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2126, Color.fromRGBA(new RGBA(255, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.7152, Color.fromRGBA(new RGBA(0, 255, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0722, Color.fromRGBA(new RGBA(0, 0, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.9278, Color.fromRGBA({ r: 255, g: 255, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.7874, Color.fromRGBA({ r: 0, g: 255, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2848, Color.fromRGBA({ r: 255, g: 0, b: 255, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.9278, Color.fromRGBA(new RGBA(255, 255, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.7874, Color.fromRGBA(new RGBA(0, 255, 255, 255)).getLuminosity());
|
||||
assert.deepEqual(0.2848, Color.fromRGBA(new RGBA(255, 0, 255, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.5271, Color.fromRGBA({ r: 192, g: 192, b: 192, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.5271, Color.fromRGBA(new RGBA(192, 192, 192, 255)).getLuminosity());
|
||||
|
||||
assert.deepEqual(0.2159, Color.fromRGBA({ r: 128, g: 128, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0459, Color.fromRGBA({ r: 128, g: 0, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2003, Color.fromRGBA({ r: 128, g: 128, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.1544, Color.fromRGBA({ r: 0, g: 128, b: 0, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0615, Color.fromRGBA({ r: 128, g: 0, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.17, Color.fromRGBA({ r: 0, g: 128, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.0156, Color.fromRGBA({ r: 0, g: 0, b: 128, a: 1 }).getLuminosity());
|
||||
assert.deepEqual(0.2159, Color.fromRGBA(new RGBA(128, 128, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0459, Color.fromRGBA(new RGBA(128, 0, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.2003, Color.fromRGBA(new RGBA(128, 128, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.1544, Color.fromRGBA(new RGBA(0, 128, 0, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0615, Color.fromRGBA(new RGBA(128, 0, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.17, Color.fromRGBA(new RGBA(0, 128, 128, 255)).getLuminosity());
|
||||
assert.deepEqual(0.0156, Color.fromRGBA(new RGBA(0, 0, 128, 255)).getLuminosity());
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -226,7 +226,7 @@ export class Configuration extends CommonEditorConfiguration {
|
||||
domNode.style.lineHeight = fontInfo.lineHeight + 'px';
|
||||
}
|
||||
|
||||
public static applyFontInfo(domNode: FastDomNode, fontInfo: BareFontInfo): void {
|
||||
public static applyFontInfo(domNode: FastDomNode<HTMLElement>, fontInfo: BareFontInfo): void {
|
||||
domNode.setFontFamily(fontInfo.fontFamily);
|
||||
domNode.setFontWeight(fontInfo.fontWeight);
|
||||
domNode.setFontSize(fontInfo.fontSize);
|
||||
@@ -292,6 +292,10 @@ export class Configuration extends CommonEditorConfiguration {
|
||||
return browser.canUseTranslate3d && browser.getZoomLevel() === 0;
|
||||
}
|
||||
|
||||
protected _getPixelRatio(): number {
|
||||
return browser.getPixelRatio();
|
||||
}
|
||||
|
||||
protected readConfiguration(bareFontInfo: BareFontInfo): FontInfo {
|
||||
return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,12 @@ class ClipboardEventWrapper implements IClipboardEvent {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setTextData(text: string): void {
|
||||
public setTextData(text: string, richText: string): void {
|
||||
if (this._event.clipboardData) {
|
||||
this._event.clipboardData.setData('text/plain', text);
|
||||
if (richText !== null) {
|
||||
this._event.clipboardData.setData('text/html', richText);
|
||||
}
|
||||
this._event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Theme, IThemeRule } from 'vs/editor/common/modes/supports/tokenization';
|
||||
import { Theme, IThemeRule, generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization';
|
||||
import { IStandaloneColorService, BuiltinTheme, ITheme } from 'vs/editor/common/services/standaloneColorService';
|
||||
import { vs, vs_dark, hc_black } from 'vs/editor/common/standalone/themes';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
@@ -61,18 +61,6 @@ export class StandaloneColorServiceImpl implements IStandaloneColorService {
|
||||
this.setTheme(VS_THEME_NAME);
|
||||
}
|
||||
|
||||
private static _generateCSS(colorMap: string[]): string {
|
||||
let rules: string[] = [];
|
||||
for (let i = 1, len = colorMap.length; i < len; i++) {
|
||||
let color = colorMap[i];
|
||||
rules[i] = `.mtk${i} { color: #${color}; }`;
|
||||
}
|
||||
rules.push('.mtki { font-style: italic; }');
|
||||
rules.push('.mtkb { font-weight: bold; }');
|
||||
rules.push('.mtku { text-decoration: underline; }');
|
||||
return rules.join('\n');
|
||||
}
|
||||
|
||||
public defineTheme(themeName: string, themeData: ITheme): void {
|
||||
if (!/^[a-z0-9\-]+$/i.test(themeName) || isBuiltinTheme(themeName)) {
|
||||
throw new Error('Illegal theme name!');
|
||||
@@ -107,7 +95,7 @@ export class StandaloneColorServiceImpl implements IStandaloneColorService {
|
||||
|
||||
this._theme = Theme.createFromRawTheme(themeData.rules);
|
||||
let colorMap = this._theme.getColorMap();
|
||||
let cssRules = StandaloneColorServiceImpl._generateCSS(colorMap);
|
||||
let cssRules = generateTokensCSSForColorMap(colorMap);
|
||||
this._styleElement.innerHTML = cssRules;
|
||||
|
||||
TokenizationRegistry.setColorMap(colorMap);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IModel } from 'vs/editor/common/editorCommon';
|
||||
import { TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes';
|
||||
import { ColorId, MetadataConsts, FontStyle, TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { renderViewLine, RenderLineInput } from 'vs/editor/common/viewLayout/viewLineRenderer';
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
@@ -56,7 +56,7 @@ export class Colorizer {
|
||||
|
||||
return new TPromise<void>((c, e, p) => {
|
||||
listener = TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.languages.indexOf(language) >= 0) {
|
||||
if (e.changedLanguages.indexOf(language) >= 0) {
|
||||
stopListening();
|
||||
c(void 0);
|
||||
}
|
||||
@@ -126,6 +126,12 @@ function _colorize(lines: string[], tabSize: number, tokenizationSupport: IToken
|
||||
function _fakeColorize(lines: string[], tabSize: number): string {
|
||||
let html: string[] = [];
|
||||
|
||||
const defaultMetadata = (
|
||||
(FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
|
||||
| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
|
||||
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
|
||||
) >>> 0;
|
||||
|
||||
for (let i = 0, length = lines.length; i < length; i++) {
|
||||
let line = lines[i];
|
||||
|
||||
@@ -134,7 +140,7 @@ function _fakeColorize(lines: string[], tabSize: number): string {
|
||||
line,
|
||||
false,
|
||||
0,
|
||||
[new ViewLineToken(line.length, '')],
|
||||
[new ViewLineToken(line.length, defaultMetadata)],
|
||||
[],
|
||||
tabSize,
|
||||
0,
|
||||
@@ -153,12 +159,11 @@ function _fakeColorize(lines: string[], tabSize: number): string {
|
||||
function _actualColorize(lines: string[], tabSize: number, tokenizationSupport: ITokenizationSupport): string {
|
||||
let html: string[] = [];
|
||||
let state = tokenizationSupport.getInitialState();
|
||||
let colorMap = TokenizationRegistry.getColorMap();
|
||||
|
||||
for (let i = 0, length = lines.length; i < length; i++) {
|
||||
let line = lines[i];
|
||||
let tokenizeResult = tokenizationSupport.tokenize2(line, state, 0);
|
||||
let lineTokens = new LineTokens(colorMap, tokenizeResult.tokens, line);
|
||||
let lineTokens = new LineTokens(tokenizeResult.tokens, line);
|
||||
let renderResult = renderViewLine(new RenderLineInput(
|
||||
false,
|
||||
line,
|
||||
|
||||
@@ -344,9 +344,11 @@ export function createMonacoEditorAPI(): typeof monaco.editor {
|
||||
TextEditorCursorBlinkingStyle: editorCommon.TextEditorCursorBlinkingStyle,
|
||||
ContentWidgetPositionPreference: ContentWidgetPositionPreference,
|
||||
OverlayWidgetPositionPreference: OverlayWidgetPositionPreference,
|
||||
RenderMinimap: editorCommon.RenderMinimap,
|
||||
|
||||
// classes
|
||||
InternalEditorScrollbarOptions: <any>editorCommon.InternalEditorScrollbarOptions,
|
||||
InternalEditorMinimapOptions: <any>editorCommon.InternalEditorMinimapOptions,
|
||||
EditorWrappingInfo: <any>editorCommon.EditorWrappingInfo,
|
||||
InternalEditorViewOptions: <any>editorCommon.InternalEditorViewOptions,
|
||||
EditorContribOptions: <any>editorCommon.EditorContribOptions,
|
||||
|
||||
@@ -49,6 +49,7 @@ import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler
|
||||
import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents';
|
||||
import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData';
|
||||
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
|
||||
import { Minimap } from 'vs/editor/browser/viewParts/minimap/minimap';
|
||||
|
||||
export class View extends ViewEventHandler implements editorBrowser.IView, IDisposable {
|
||||
|
||||
@@ -270,6 +271,9 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
|
||||
let rulers = new Rulers(this._context);
|
||||
this.viewParts.push(rulers);
|
||||
|
||||
let minimap = new Minimap(this._context, this.layoutProvider, this._scrollbar);
|
||||
this.viewParts.push(minimap);
|
||||
|
||||
// -------------- Wire dom nodes up
|
||||
|
||||
this.linesContentContainer = this._scrollbar.getScrollbarContainerDomNode();
|
||||
@@ -292,6 +296,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
|
||||
this.overflowGuardContainer.appendChild(this.overlayWidgets.domNode);
|
||||
this.overflowGuardContainer.appendChild(this.textArea);
|
||||
this.overflowGuardContainer.appendChild(this.textAreaCover);
|
||||
this.overflowGuardContainer.appendChild(minimap.getDomNode());
|
||||
this.domNode.appendChild(this.overflowGuardContainer);
|
||||
this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode);
|
||||
}
|
||||
@@ -477,7 +482,7 @@ export class View extends ViewEventHandler implements editorBrowser.IView, IDisp
|
||||
StyleMutator.setHeight(this.linesContent, 1000000);
|
||||
|
||||
StyleMutator.setLeft(this.linesContentContainer, layoutInfo.contentLeft);
|
||||
StyleMutator.setWidth(this.linesContentContainer, layoutInfo.contentWidth);
|
||||
StyleMutator.setWidth(this.linesContentContainer, layoutInfo.contentWidth + layoutInfo.minimapWidth);
|
||||
StyleMutator.setHeight(this.linesContentContainer, layoutInfo.contentHeight);
|
||||
|
||||
this.outgoingEvents.emitViewLayoutChanged(layoutInfo);
|
||||
|
||||
@@ -108,7 +108,19 @@ export class RenderedLinesCollection<T extends ILine> {
|
||||
let startLineNumber = this.getStartLineNumber();
|
||||
let endLineNumber = this.getEndLineNumber();
|
||||
|
||||
// Record what needs to be deleted, notify lines that survive after deletion
|
||||
if (deleteToLineNumber < startLineNumber) {
|
||||
// deleting above the viewport
|
||||
let deleteCnt = deleteToLineNumber - deleteFromLineNumber + 1;
|
||||
this._rendLineNumberStart -= deleteCnt;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (deleteFromLineNumber > endLineNumber) {
|
||||
// deleted below the viewport
|
||||
return null;
|
||||
}
|
||||
|
||||
// Record what needs to be deleted
|
||||
let deleteStartIndex = 0;
|
||||
let deleteCount = 0;
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
@@ -154,18 +166,14 @@ export class RenderedLinesCollection<T extends ILine> {
|
||||
let startLineNumber = this.getStartLineNumber();
|
||||
let endLineNumber = this.getEndLineNumber();
|
||||
|
||||
// Notify lines after the change
|
||||
let notifiedSomeone = false;
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
let lineIndex = lineNumber - this._rendLineNumberStart;
|
||||
|
||||
if (lineNumber === changedLineNumber) {
|
||||
this._lines[lineIndex].onContentChanged();
|
||||
notifiedSomeone = true;
|
||||
}
|
||||
if (changedLineNumber < startLineNumber || changedLineNumber > endLineNumber) {
|
||||
// a line has been changed above or below the viewport
|
||||
return false;
|
||||
}
|
||||
|
||||
return notifiedSomeone;
|
||||
// Notify the line
|
||||
this._lines[changedLineNumber - this._rendLineNumberStart].onContentChanged();
|
||||
return true;
|
||||
}
|
||||
|
||||
public onModelLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): T[] {
|
||||
@@ -244,7 +252,7 @@ export class RenderedLinesCollection<T extends ILine> {
|
||||
|
||||
export abstract class ViewLayer<T extends IVisibleLine> extends ViewPart {
|
||||
|
||||
protected domNode: FastDomNode;
|
||||
protected domNode: FastDomNode<HTMLElement>;
|
||||
protected _linesCollection: RenderedLinesCollection<T>;
|
||||
private _renderer: ViewLayerRenderer<T>;
|
||||
private _scrollDomNode: HTMLElement;
|
||||
@@ -353,7 +361,7 @@ export abstract class ViewLayer<T extends IVisibleLine> extends ViewPart {
|
||||
this._scrollDomNodeIsAbove = resCtx.scrollDomNodeIsAbove;
|
||||
}
|
||||
|
||||
private _createDomNode(): FastDomNode {
|
||||
private _createDomNode(): FastDomNode<HTMLElement> {
|
||||
let domNode = createFastDomNode(document.createElement('div'));
|
||||
domNode.setClassName('view-layer');
|
||||
domNode.setPosition('absolute');
|
||||
|
||||
@@ -114,7 +114,7 @@ export class ViewOverlayLine implements IVisibleLine {
|
||||
|
||||
private _configuration: IConfiguration;
|
||||
private _dynamicOverlays: DynamicViewOverlay[];
|
||||
private _domNode: FastDomNode;
|
||||
private _domNode: FastDomNode<HTMLElement>;
|
||||
private _renderedContent: string;
|
||||
private _lineHeight: number;
|
||||
|
||||
|
||||
@@ -390,10 +390,6 @@ export class ViewContentWidgets extends ViewPart {
|
||||
}
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
let data: IMyRenderData = {};
|
||||
|
||||
let keys = Object.keys(this._widgets);
|
||||
|
||||
@@ -104,9 +104,6 @@ export class CurrentLineHighlightOverlay extends DynamicViewOverlay {
|
||||
// --- end event handlers
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
this._scrollWidth = ctx.scrollWidth;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,10 +78,6 @@ export class DecorationsOverlay extends DynamicViewOverlay {
|
||||
// --- end event handlers
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
let _decorations = ctx.getDecorationsInViewport();
|
||||
|
||||
// Keep only decorations with `className`
|
||||
|
||||
@@ -125,4 +125,8 @@ export class EditorScrollbar implements IDisposable {
|
||||
public delegateVerticalScrollbarMouseDown(browserEvent: MouseEvent): void {
|
||||
this.scrollbar.delegateVerticalScrollbarMouseDown(browserEvent);
|
||||
}
|
||||
|
||||
public getVerticalSliderVerticalCenter(): number {
|
||||
return this.scrollbar.getVerticalSliderVerticalCenter();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,10 +163,6 @@ export class GlyphMarginOverlay extends DedupOverlay {
|
||||
}
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
if (!this._glyphMargin) {
|
||||
this._renderResult = null;
|
||||
return;
|
||||
|
||||
@@ -73,10 +73,6 @@ export class IndentGuidesOverlay extends DynamicViewOverlay {
|
||||
// --- end event handlers
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
if (!this._enabled) {
|
||||
this._renderResult = null;
|
||||
return;
|
||||
|
||||
@@ -231,7 +231,7 @@ export class ViewLine implements IVisibleLine {
|
||||
}
|
||||
|
||||
interface IRenderedViewLine {
|
||||
domNode: FastDomNode;
|
||||
domNode: FastDomNode<HTMLElement>;
|
||||
readonly input: RenderLineInput;
|
||||
getWidth(): number;
|
||||
getVisibleRangesForRange(startColumn: number, endColumn: number, context: DomReadingContext): HorizontalRange[];
|
||||
@@ -243,14 +243,14 @@ interface IRenderedViewLine {
|
||||
*/
|
||||
class FastRenderedViewLine implements IRenderedViewLine {
|
||||
|
||||
public domNode: FastDomNode;
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
public readonly input: RenderLineInput;
|
||||
|
||||
private readonly _characterMapping: CharacterMapping;
|
||||
private readonly _charWidth: number;
|
||||
private _charOffset: Uint32Array;
|
||||
|
||||
constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping) {
|
||||
constructor(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping) {
|
||||
this.domNode = domNode;
|
||||
this.input = renderLineInput;
|
||||
|
||||
@@ -345,7 +345,7 @@ class FastRenderedViewLine implements IRenderedViewLine {
|
||||
*/
|
||||
class RenderedViewLine {
|
||||
|
||||
public domNode: FastDomNode;
|
||||
public domNode: FastDomNode<HTMLElement>;
|
||||
public readonly input: RenderLineInput;
|
||||
|
||||
protected readonly _characterMapping: CharacterMapping;
|
||||
@@ -357,7 +357,7 @@ class RenderedViewLine {
|
||||
*/
|
||||
private _pixelOffsetCache: Int32Array;
|
||||
|
||||
constructor(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean) {
|
||||
constructor(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean) {
|
||||
this.domNode = domNode;
|
||||
this.input = renderLineInput;
|
||||
this._characterMapping = characterMapping;
|
||||
@@ -548,17 +548,17 @@ class WebKitRenderedViewLine extends RenderedViewLine {
|
||||
}
|
||||
}
|
||||
|
||||
const createRenderedLine: (domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean) => RenderedViewLine = (function () {
|
||||
const createRenderedLine: (domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean) => RenderedViewLine = (function () {
|
||||
if (browser.isWebKit) {
|
||||
return createWebKitRenderedLine;
|
||||
}
|
||||
return createNormalRenderedLine;
|
||||
})();
|
||||
|
||||
function createWebKitRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean): RenderedViewLine {
|
||||
function createWebKitRenderedLine(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean): RenderedViewLine {
|
||||
return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL);
|
||||
}
|
||||
|
||||
function createNormalRenderedLine(domNode: FastDomNode, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean): RenderedViewLine {
|
||||
function createNormalRenderedLine(domNode: FastDomNode<HTMLElement>, renderLineInput: RenderLineInput, characterMapping: CharacterMapping, containsRTL: boolean): RenderedViewLine {
|
||||
return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL);
|
||||
}
|
||||
|
||||
@@ -412,10 +412,6 @@ export class ViewLines extends ViewLayer<ViewLine> implements IViewLines {
|
||||
}
|
||||
|
||||
public renderText(viewportData: ViewportData, onAfterLinesRendered: () => void): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
// (1) render lines - ensures lines are in the DOM
|
||||
super._renderLines(viewportData);
|
||||
this._lastRenderedData.setBigNumbersDelta(viewportData.bigNumbersDelta);
|
||||
|
||||
@@ -18,7 +18,7 @@ export class Margin extends ViewPart {
|
||||
private _contentLeft: number;
|
||||
private _glyphMarginLeft: number;
|
||||
private _glyphMarginWidth: number;
|
||||
private _glyphMarginBackgroundDomNode: FastDomNode;
|
||||
private _glyphMarginBackgroundDomNode: FastDomNode<HTMLElement>;
|
||||
|
||||
constructor(context: ViewContext) {
|
||||
super(context);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-editor.vs .minimap-slider {
|
||||
background: rgba(100, 100, 100, .4);
|
||||
}
|
||||
.monaco-editor.vs-dark .minimap-slider {
|
||||
background: rgba(121, 121, 121, .4);
|
||||
}
|
||||
.monaco-editor.hc-black .minimap-slider {
|
||||
background: rgba(111, 195, 223, .6);
|
||||
}
|
||||
|
||||
.monaco-editor.vs .minimap-slider:hover,
|
||||
.monaco-editor.vs-dark .minimap-slider:hover {
|
||||
background: rgba(100, 100, 100, .7);
|
||||
}
|
||||
.monaco-editor.hc-black .minimap-slider:hover {
|
||||
background: rgba(111, 195, 223, .8);
|
||||
}
|
||||
|
||||
.monaco-editor.vs .minimap-slider.active {
|
||||
background: rgba(0, 0, 0, .6);
|
||||
}
|
||||
.monaco-editor.vs-dark .minimap-slider.active {
|
||||
background: rgba(191, 191, 191, .4);
|
||||
}
|
||||
.monaco-editor.hc-black .minimap-slider.active {
|
||||
background: rgba(111, 195, 223, 1);
|
||||
}
|
||||
@@ -0,0 +1,736 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import 'vs/css!./minimap';
|
||||
import { ViewPart } from 'vs/editor/browser/view/viewPart';
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { MinimapCharRenderer, ParsedColor, MinimapTokensColorTracker, Constants } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { IViewLayout, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ColorId } from 'vs/editor/common/modes';
|
||||
import { FastDomNode, createFastDomNode } from 'vs/base/browser/styleMutator';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { EditorScrollbar } from 'vs/editor/browser/viewParts/editorScrollbar/editorScrollbar';
|
||||
import { RenderedLinesCollection, ILine } from 'vs/editor/browser/view/viewLayer';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
||||
const enum RenderMinimap {
|
||||
None = 0,
|
||||
Small = 1,
|
||||
Large = 2
|
||||
}
|
||||
|
||||
class MinimapOptions {
|
||||
|
||||
public readonly renderMinimap: RenderMinimap;
|
||||
|
||||
public readonly pixelRatio: number;
|
||||
|
||||
public readonly lineHeight: number;
|
||||
|
||||
/**
|
||||
* container dom node width (in CSS px)
|
||||
*/
|
||||
public readonly minimapWidth: number;
|
||||
/**
|
||||
* container dom node height (in CSS px)
|
||||
*/
|
||||
public readonly minimapHeight: number;
|
||||
|
||||
/**
|
||||
* canvas backing store width (in device px)
|
||||
*/
|
||||
public readonly canvasInnerWidth: number;
|
||||
/**
|
||||
* canvas backing store height (in device px)
|
||||
*/
|
||||
public readonly canvasInnerHeight: number;
|
||||
|
||||
/**
|
||||
* canvas width (in CSS px)
|
||||
*/
|
||||
public readonly canvasOuterWidth: number;
|
||||
/**
|
||||
* canvas height (in CSS px)
|
||||
*/
|
||||
public readonly canvasOuterHeight: number;
|
||||
|
||||
constructor(configuration: editorCommon.IConfiguration) {
|
||||
const pixelRatio = browser.getPixelRatio();
|
||||
const layoutInfo = configuration.editor.layoutInfo;
|
||||
|
||||
this.renderMinimap = layoutInfo.renderMinimap | 0;
|
||||
this.pixelRatio = pixelRatio;
|
||||
this.lineHeight = configuration.editor.lineHeight;
|
||||
this.minimapWidth = layoutInfo.minimapWidth;
|
||||
this.minimapHeight = layoutInfo.height;
|
||||
|
||||
this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth);
|
||||
this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight);
|
||||
|
||||
this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio;
|
||||
this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio;
|
||||
}
|
||||
|
||||
public equals(other: MinimapOptions): boolean {
|
||||
return (this.renderMinimap === other.renderMinimap
|
||||
&& this.pixelRatio === other.pixelRatio
|
||||
&& this.lineHeight === other.lineHeight
|
||||
&& this.minimapWidth === other.minimapWidth
|
||||
&& this.minimapHeight === other.minimapHeight
|
||||
&& this.canvasInnerWidth === other.canvasInnerWidth
|
||||
&& this.canvasInnerHeight === other.canvasInnerHeight
|
||||
&& this.canvasOuterWidth === other.canvasOuterWidth
|
||||
&& this.canvasOuterHeight === other.canvasOuterHeight
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MinimapLayout {
|
||||
|
||||
/**
|
||||
* slider dom node top (in CSS px)
|
||||
*/
|
||||
public readonly sliderTop: number;
|
||||
/**
|
||||
* slider dom node height (in CSS px)
|
||||
*/
|
||||
public readonly sliderHeight: number;
|
||||
|
||||
/**
|
||||
* minimap render start line number.
|
||||
*/
|
||||
public readonly startLineNumber: number;
|
||||
/**
|
||||
* minimap render end line number.
|
||||
*/
|
||||
public readonly endLineNumber: number;
|
||||
|
||||
constructor(
|
||||
lastRenderData: RenderData,
|
||||
options: MinimapOptions,
|
||||
viewportStartLineNumber: number,
|
||||
viewportEndLineNumber: number,
|
||||
viewportHeight: number,
|
||||
lineCount: number,
|
||||
scrollbarSliderCenter: number
|
||||
) {
|
||||
const pixelRatio = options.pixelRatio;
|
||||
const minimapLineHeight = (options.renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_HEIGHT : Constants.x1_CHAR_HEIGHT);
|
||||
const minimapLinesFitting = Math.floor(options.canvasInnerHeight / minimapLineHeight);
|
||||
const lineHeight = options.lineHeight;
|
||||
|
||||
// Sometimes, the number of rendered lines varies for a constant viewport height.
|
||||
// The reason is that only parts of the viewportStartLineNumber or viewportEndLineNumber are visible.
|
||||
// This leads to an apparent tremor in the minimap's slider height.
|
||||
// We try here to compensate, making the slider slightly incorrect in these cases, but more pleasing to the eye.
|
||||
let viewportLineCount = viewportEndLineNumber - viewportStartLineNumber + 1;
|
||||
const expectedViewportLineCount = Math.round(viewportHeight / lineHeight);
|
||||
if (viewportLineCount > expectedViewportLineCount) {
|
||||
viewportLineCount = expectedViewportLineCount;
|
||||
}
|
||||
|
||||
if (minimapLinesFitting >= lineCount) {
|
||||
// All lines fit in the minimap => no minimap scrolling
|
||||
this.startLineNumber = 1;
|
||||
this.endLineNumber = lineCount;
|
||||
} else {
|
||||
// The desire is to align (centers) the minimap's slider with the scrollbar's slider
|
||||
|
||||
// For a resolved this.startLineNumber, we can compute the minimap's slider's center with the following formula:
|
||||
// scrollbarSliderCenter = (viewportStartLineNumber - this.startLineNumber + viewportLineCount/2) * minimapLineHeight / pixelRatio;
|
||||
// =>
|
||||
// scrollbarSliderCenter = (viewportStartLineNumber - this.startLineNumber + viewportLineCount/2) * minimapLineHeight / pixelRatio;
|
||||
// scrollbarSliderCenter * pixelRatio / minimapLineHeight = viewportStartLineNumber - this.startLineNumber + viewportLineCount/2
|
||||
// this.startLineNumber = viewportStartLineNumber + viewportLineCount/2 - scrollbarSliderCenter * pixelRatio / minimapLineHeight
|
||||
let desiredStartLineNumber = Math.floor(viewportStartLineNumber + viewportLineCount / 2 - scrollbarSliderCenter * pixelRatio / minimapLineHeight);
|
||||
let desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
|
||||
|
||||
// Aligning the slider's centers can result (correctly) in tremor.
|
||||
// i.e. scrolling down might result in the startLineNumber going up.
|
||||
// Avoid this tremor by being consistent w.r.t. the previous computed result
|
||||
if (lastRenderData) {
|
||||
const lastLayoutDecision = lastRenderData.renderedLayout;
|
||||
if (lastLayoutDecision.viewportStartLineNumber <= viewportStartLineNumber) {
|
||||
// going down => make sure we don't go above our previous decision
|
||||
if (desiredStartLineNumber < lastLayoutDecision.startLineNumber) {
|
||||
desiredStartLineNumber = lastLayoutDecision.startLineNumber;
|
||||
desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
|
||||
}
|
||||
}
|
||||
if (lastLayoutDecision.viewportStartLineNumber >= viewportStartLineNumber) {
|
||||
// going up => make sure we don't go below our previous decision
|
||||
if (desiredEndLineNumber > lastLayoutDecision.endLineNumber) {
|
||||
desiredEndLineNumber = lastLayoutDecision.endLineNumber;
|
||||
desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aligning the slider's centers is a very good thing, but this would make
|
||||
// the minimap never scroll all the way to the top or to the bottom of the file.
|
||||
// We therefore check that the viewport lines are in the minimap viewport.
|
||||
|
||||
// (a) validate on start line number
|
||||
if (desiredStartLineNumber < 1) {
|
||||
// must start after 1
|
||||
desiredStartLineNumber = 1;
|
||||
desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
|
||||
}
|
||||
if (desiredStartLineNumber > viewportStartLineNumber) {
|
||||
// must contain the viewport's start line number
|
||||
desiredStartLineNumber = viewportStartLineNumber;
|
||||
desiredEndLineNumber = desiredStartLineNumber + minimapLinesFitting - 1;
|
||||
}
|
||||
|
||||
// (b) validate on end line number
|
||||
if (desiredEndLineNumber > lineCount) {
|
||||
// must end before line count
|
||||
desiredEndLineNumber = lineCount;
|
||||
desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
|
||||
}
|
||||
if (desiredEndLineNumber < viewportEndLineNumber) {
|
||||
// must contain the viewport's end line number
|
||||
desiredEndLineNumber = viewportEndLineNumber;
|
||||
desiredStartLineNumber = desiredEndLineNumber - minimapLinesFitting + 1;
|
||||
}
|
||||
|
||||
this.startLineNumber = desiredStartLineNumber;
|
||||
this.endLineNumber = desiredEndLineNumber;
|
||||
}
|
||||
|
||||
this.sliderTop = Math.floor((viewportStartLineNumber - this.startLineNumber) * minimapLineHeight / pixelRatio);
|
||||
this.sliderHeight = Math.floor(viewportLineCount * minimapLineHeight / pixelRatio);
|
||||
}
|
||||
}
|
||||
|
||||
class RenderedLayout {
|
||||
/**
|
||||
* editor viewport start line number.
|
||||
*/
|
||||
public readonly viewportStartLineNumber: number;
|
||||
/**
|
||||
* editor viewport end line number.
|
||||
*/
|
||||
public readonly viewportEndLineNumber: number;
|
||||
|
||||
/**
|
||||
* minimap rendered start line number.
|
||||
*/
|
||||
public readonly startLineNumber: number;
|
||||
/**
|
||||
* minimap rendered end line number.
|
||||
*/
|
||||
public readonly endLineNumber: number;
|
||||
|
||||
constructor(
|
||||
viewportStartLineNumber: number,
|
||||
viewportEndLineNumber: number,
|
||||
startLineNumber: number,
|
||||
endLineNumber: number
|
||||
) {
|
||||
this.viewportStartLineNumber = viewportStartLineNumber;
|
||||
this.viewportEndLineNumber = viewportEndLineNumber;
|
||||
this.startLineNumber = startLineNumber;
|
||||
this.endLineNumber = endLineNumber;
|
||||
}
|
||||
}
|
||||
|
||||
class MinimapLine implements ILine {
|
||||
|
||||
public static INVALID = new MinimapLine(-1);
|
||||
|
||||
dy: number;
|
||||
|
||||
constructor(dy: number) {
|
||||
this.dy = dy;
|
||||
}
|
||||
|
||||
public onContentChanged(): void {
|
||||
this.dy = -1;
|
||||
}
|
||||
|
||||
public onTokensChanged(): void {
|
||||
this.dy = -1;
|
||||
}
|
||||
}
|
||||
|
||||
class RenderData {
|
||||
/**
|
||||
* last rendered layout.
|
||||
*/
|
||||
public readonly renderedLayout: RenderedLayout;
|
||||
private readonly _imageData: ImageData;
|
||||
private readonly _renderedLines: RenderedLinesCollection<MinimapLine>;
|
||||
|
||||
constructor(
|
||||
renderedLayout: RenderedLayout,
|
||||
imageData: ImageData,
|
||||
lines: MinimapLine[]
|
||||
) {
|
||||
this.renderedLayout = renderedLayout;
|
||||
this._imageData = imageData;
|
||||
this._renderedLines = new RenderedLinesCollection(
|
||||
() => MinimapLine.INVALID
|
||||
);
|
||||
this._renderedLines._set(renderedLayout.startLineNumber, lines);
|
||||
}
|
||||
|
||||
_get(): { imageData: ImageData; rendLineNumberStart: number; lines: MinimapLine[]; } {
|
||||
let tmp = this._renderedLines._get();
|
||||
return {
|
||||
imageData: this._imageData,
|
||||
rendLineNumberStart: tmp.rendLineNumberStart,
|
||||
lines: tmp.lines
|
||||
};
|
||||
}
|
||||
|
||||
public onModelLinesDeleted(e: editorCommon.IViewLinesDeletedEvent): void {
|
||||
this._renderedLines.onModelLinesDeleted(e.fromLineNumber, e.toLineNumber);
|
||||
}
|
||||
public onModelLineChanged(e: editorCommon.IViewLineChangedEvent): boolean {
|
||||
return this._renderedLines.onModelLineChanged(e.lineNumber);
|
||||
}
|
||||
public onModelLinesInserted(e: editorCommon.IViewLinesInsertedEvent): void {
|
||||
this._renderedLines.onModelLinesInserted(e.fromLineNumber, e.toLineNumber);
|
||||
}
|
||||
public onModelTokensChanged(e: editorCommon.IViewTokensChangedEvent): boolean {
|
||||
return this._renderedLines.onModelTokensChanged(e.ranges);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some sort of double buffering.
|
||||
*
|
||||
* Keeps two buffers around that will be rotated for painting.
|
||||
* Always gives a buffer that is filled with the background color.
|
||||
*/
|
||||
class MinimapBuffers {
|
||||
|
||||
private readonly _backgroundFillData: Uint8ClampedArray;
|
||||
private readonly _buffers: [ImageData, ImageData];
|
||||
private _lastUsedBuffer: number;
|
||||
|
||||
constructor(ctx: CanvasRenderingContext2D, WIDTH: number, HEIGHT: number, background: ParsedColor) {
|
||||
this._backgroundFillData = MinimapBuffers._createBackgroundFillData(WIDTH, HEIGHT, background);
|
||||
this._buffers = [
|
||||
ctx.createImageData(WIDTH, HEIGHT),
|
||||
ctx.createImageData(WIDTH, HEIGHT)
|
||||
];
|
||||
this._lastUsedBuffer = 0;
|
||||
}
|
||||
|
||||
public getBuffer(): ImageData {
|
||||
// rotate buffers
|
||||
this._lastUsedBuffer = 1 - this._lastUsedBuffer;
|
||||
let result = this._buffers[this._lastUsedBuffer];
|
||||
|
||||
// fill with background color
|
||||
result.data.set(this._backgroundFillData);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static _createBackgroundFillData(WIDTH: number, HEIGHT: number, background: ParsedColor): Uint8ClampedArray {
|
||||
const backgroundR = background.r;
|
||||
const backgroundG = background.g;
|
||||
const backgroundB = background.b;
|
||||
|
||||
let result = new Uint8ClampedArray(WIDTH * HEIGHT * 4);
|
||||
let offset = 0;
|
||||
for (let i = 0; i < HEIGHT; i++) {
|
||||
for (let j = 0; j < WIDTH; j++) {
|
||||
result[offset] = backgroundR;
|
||||
result[offset + 1] = backgroundG;
|
||||
result[offset + 2] = backgroundB;
|
||||
result[offset + 3] = 255;
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class Minimap extends ViewPart {
|
||||
|
||||
private readonly _viewLayout: IViewLayout;
|
||||
private readonly _editorScrollbar: EditorScrollbar;
|
||||
|
||||
private readonly _domNode: FastDomNode<HTMLElement>;
|
||||
private readonly _canvas: FastDomNode<HTMLCanvasElement>;
|
||||
private readonly _slider: FastDomNode<HTMLElement>;
|
||||
private readonly _tokensColorTracker: MinimapTokensColorTracker;
|
||||
private readonly _tokensColorTrackerListener: IDisposable;
|
||||
private readonly _mouseDownListener: IDisposable;
|
||||
|
||||
private readonly _minimapCharRenderer: MinimapCharRenderer;
|
||||
|
||||
private _options: MinimapOptions;
|
||||
private _lastRenderData: RenderData;
|
||||
private _buffers: MinimapBuffers;
|
||||
|
||||
constructor(context: ViewContext, viewLayout: IViewLayout, editorScrollbar: EditorScrollbar) {
|
||||
super(context);
|
||||
this._viewLayout = viewLayout;
|
||||
this._editorScrollbar = editorScrollbar;
|
||||
|
||||
this._options = new MinimapOptions(this._context.configuration);
|
||||
this._lastRenderData = null;
|
||||
this._buffers = null;
|
||||
|
||||
this._domNode = createFastDomNode(document.createElement('div'));
|
||||
this._domNode.setPosition('absolute');
|
||||
this._domNode.setRight(this._context.configuration.editor.layoutInfo.verticalScrollbarWidth);
|
||||
|
||||
this._canvas = createFastDomNode(document.createElement('canvas'));
|
||||
this._canvas.setPosition('absolute');
|
||||
this._canvas.setLeft(0);
|
||||
this._domNode.domNode.appendChild(this._canvas.domNode);
|
||||
|
||||
this._slider = createFastDomNode(document.createElement('div'));
|
||||
this._slider.setPosition('absolute');
|
||||
this._slider.setClassName('minimap-slider');
|
||||
this._domNode.domNode.appendChild(this._slider.domNode);
|
||||
|
||||
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
|
||||
this._tokensColorTrackerListener = this._tokensColorTracker.onDidChange(() => this._buffers = null);
|
||||
|
||||
this._minimapCharRenderer = getOrCreateMinimapCharRenderer();
|
||||
|
||||
this._applyLayout();
|
||||
|
||||
this._mouseDownListener = dom.addStandardDisposableListener(this._canvas.domNode, 'mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const renderMinimap = this._options.renderMinimap;
|
||||
if (renderMinimap === RenderMinimap.None) {
|
||||
return;
|
||||
}
|
||||
if (!this._lastRenderData) {
|
||||
return;
|
||||
}
|
||||
const minimapLineHeight = (renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_HEIGHT : Constants.x1_CHAR_HEIGHT);
|
||||
const internalOffsetY = this._options.pixelRatio * e.browserEvent.offsetY;
|
||||
const lineIndex = Math.floor(internalOffsetY / minimapLineHeight);
|
||||
|
||||
let lineNumber = lineIndex + this._lastRenderData.renderedLayout.startLineNumber;
|
||||
lineNumber = Math.min(lineNumber, this._context.model.getLineCount());
|
||||
|
||||
let revealPositionEvent: editorCommon.IViewRevealRangeEvent = {
|
||||
range: new Range(lineNumber, 1, lineNumber, 1),
|
||||
verticalType: editorCommon.VerticalRevealType.Center,
|
||||
revealHorizontal: false,
|
||||
revealCursor: false
|
||||
};
|
||||
this._context.privateViewEventBus.emit(editorCommon.ViewEventNames.RevealRangeEvent, revealPositionEvent);
|
||||
});
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._tokensColorTrackerListener.dispose();
|
||||
this._mouseDownListener.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public getDomNode(): HTMLElement {
|
||||
return this._domNode.domNode;
|
||||
}
|
||||
|
||||
private _applyLayout(): void {
|
||||
this._domNode.setWidth(this._options.minimapWidth);
|
||||
this._domNode.setHeight(this._options.minimapHeight);
|
||||
this._canvas.setWidth(this._options.canvasOuterWidth);
|
||||
this._canvas.setHeight(this._options.canvasOuterHeight);
|
||||
this._canvas.domNode.width = this._options.canvasInnerWidth;
|
||||
this._canvas.domNode.height = this._options.canvasInnerHeight;
|
||||
this._slider.setWidth(this._options.minimapWidth);
|
||||
this._buffers = null;
|
||||
}
|
||||
|
||||
private _getBuffer(): ImageData {
|
||||
if (!this._buffers) {
|
||||
this._buffers = new MinimapBuffers(
|
||||
this._canvas.domNode.getContext('2d'),
|
||||
this._options.canvasInnerWidth,
|
||||
this._options.canvasInnerHeight,
|
||||
this._tokensColorTracker.getColor(ColorId.DefaultBackground)
|
||||
);
|
||||
}
|
||||
return this._buffers.getBuffer();
|
||||
}
|
||||
|
||||
// ---- begin view event handlers
|
||||
|
||||
private _onOptionsMaybeChanged(): boolean {
|
||||
let opts = new MinimapOptions(this._context.configuration);
|
||||
if (this._options.equals(opts)) {
|
||||
return false;
|
||||
}
|
||||
this._options = opts;
|
||||
this._lastRenderData = null;
|
||||
this._applyLayout();
|
||||
return true;
|
||||
}
|
||||
|
||||
public onLineMappingChanged(): boolean {
|
||||
this._lastRenderData = null;
|
||||
return true;
|
||||
}
|
||||
public onModelFlushed(): boolean {
|
||||
this._lastRenderData = null;
|
||||
return true;
|
||||
}
|
||||
public onModelLinesDeleted(e: editorCommon.IViewLinesDeletedEvent): boolean {
|
||||
if (this._lastRenderData) {
|
||||
this._lastRenderData.onModelLinesDeleted(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public onModelLineChanged(e: editorCommon.IViewLineChangedEvent): boolean {
|
||||
if (this._lastRenderData) {
|
||||
return this._lastRenderData.onModelLineChanged(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onModelLinesInserted(e: editorCommon.IViewLinesInsertedEvent): boolean {
|
||||
if (this._lastRenderData) {
|
||||
this._lastRenderData.onModelLinesInserted(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public onModelTokensChanged(e: editorCommon.IViewTokensChangedEvent): boolean {
|
||||
if (this._lastRenderData) {
|
||||
return this._lastRenderData.onModelTokensChanged(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public onConfigurationChanged(e: editorCommon.IConfigurationChangedEvent): boolean {
|
||||
return this._onOptionsMaybeChanged();
|
||||
}
|
||||
public onLayoutChanged(layoutInfo: editorCommon.EditorLayoutInfo): boolean {
|
||||
return this._onOptionsMaybeChanged();
|
||||
}
|
||||
public onScrollChanged(e: editorCommon.IScrollEvent): boolean {
|
||||
return e.scrollTopChanged || e.scrollHeightChanged;
|
||||
}
|
||||
public onZonesChanged(): boolean {
|
||||
this._lastRenderData = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// --- end event handlers
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
}
|
||||
|
||||
public render(renderingCtx: IRestrictedRenderingContext): void {
|
||||
const renderMinimap = this._options.renderMinimap;
|
||||
if (renderMinimap === RenderMinimap.None) {
|
||||
return;
|
||||
}
|
||||
|
||||
const layout = new MinimapLayout(
|
||||
this._lastRenderData,
|
||||
this._options,
|
||||
renderingCtx.visibleRange.startLineNumber,
|
||||
renderingCtx.visibleRange.endLineNumber,
|
||||
renderingCtx.viewportHeight,
|
||||
this._context.model.getLineCount(),
|
||||
this._editorScrollbar.getVerticalSliderVerticalCenter()
|
||||
);
|
||||
this._slider.setTop(layout.sliderTop);
|
||||
this._slider.setHeight(layout.sliderHeight);
|
||||
|
||||
const startLineNumber = layout.startLineNumber;
|
||||
const endLineNumber = layout.endLineNumber;
|
||||
const minimapLineHeight = (renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_HEIGHT : Constants.x1_CHAR_HEIGHT);
|
||||
|
||||
const imageData = this._getBuffer();
|
||||
|
||||
// Render untouched lines by using last rendered data.
|
||||
let needed = Minimap._renderUntouchedLines(
|
||||
imageData,
|
||||
startLineNumber,
|
||||
endLineNumber,
|
||||
minimapLineHeight,
|
||||
this._lastRenderData
|
||||
);
|
||||
|
||||
// Fetch rendering info from view model for rest of lines that need rendering.
|
||||
const lineInfo = this._context.model.getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed);
|
||||
const tabSize = lineInfo.tabSize;
|
||||
const background = this._tokensColorTracker.getColor(ColorId.DefaultBackground);
|
||||
|
||||
// Render the rest of lines
|
||||
let dy = 0;
|
||||
let renderedLines: MinimapLine[] = [];
|
||||
for (let lineIndex = 0, lineCount = endLineNumber - startLineNumber + 1; lineIndex < lineCount; lineIndex++) {
|
||||
if (needed[lineIndex]) {
|
||||
Minimap._renderLine(
|
||||
imageData,
|
||||
background,
|
||||
renderMinimap,
|
||||
this._tokensColorTracker,
|
||||
this._minimapCharRenderer,
|
||||
dy,
|
||||
tabSize,
|
||||
lineInfo.data[lineIndex]
|
||||
);
|
||||
}
|
||||
renderedLines[lineIndex] = new MinimapLine(dy);
|
||||
dy += minimapLineHeight;
|
||||
}
|
||||
|
||||
// Save rendered data for reuse on next frame if possible
|
||||
this._lastRenderData = new RenderData(
|
||||
new RenderedLayout(
|
||||
renderingCtx.visibleRange.startLineNumber,
|
||||
renderingCtx.visibleRange.endLineNumber,
|
||||
startLineNumber,
|
||||
endLineNumber
|
||||
),
|
||||
imageData,
|
||||
renderedLines
|
||||
);
|
||||
|
||||
// Finally, paint to the canvas
|
||||
const ctx = this._canvas.domNode.getContext('2d');
|
||||
ctx.putImageData(imageData, 0, 0);
|
||||
}
|
||||
|
||||
private static _renderUntouchedLines(
|
||||
target: ImageData,
|
||||
startLineNumber: number,
|
||||
endLineNumber: number,
|
||||
minimapLineHeight: number,
|
||||
lastRenderData: RenderData,
|
||||
): boolean[] {
|
||||
|
||||
let needed: boolean[] = [];
|
||||
if (!lastRenderData) {
|
||||
for (let i = 0, len = endLineNumber - startLineNumber + 1; i < len; i++) {
|
||||
needed[i] = true;
|
||||
}
|
||||
return needed;
|
||||
}
|
||||
|
||||
const _lastData = lastRenderData._get();
|
||||
const lastTargetData = _lastData.imageData.data;
|
||||
const lastStartLineNumber = _lastData.rendLineNumberStart;
|
||||
const lastLines = _lastData.lines;
|
||||
const lastLinesLength = lastLines.length;
|
||||
const WIDTH = target.width;
|
||||
const targetData = target.data;
|
||||
|
||||
let copySourceStart = -1;
|
||||
let copySourceEnd = -1;
|
||||
let copyDestStart = -1;
|
||||
let copyDestEnd = -1;
|
||||
|
||||
let dest_dy = 0;
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
const lineIndex = lineNumber - startLineNumber;
|
||||
const lastLineIndex = lineNumber - lastStartLineNumber;
|
||||
const source_dy = (lastLineIndex >= 0 && lastLineIndex < lastLinesLength ? lastLines[lastLineIndex].dy : -1);
|
||||
|
||||
if (source_dy === -1) {
|
||||
needed[lineIndex] = true;
|
||||
dest_dy += minimapLineHeight;
|
||||
continue;
|
||||
}
|
||||
|
||||
let sourceStart = source_dy * WIDTH * 4;
|
||||
let sourceEnd = (source_dy + minimapLineHeight) * WIDTH * 4;
|
||||
let destStart = dest_dy * WIDTH * 4;
|
||||
let destEnd = (dest_dy + minimapLineHeight) * WIDTH * 4;
|
||||
|
||||
if (copySourceEnd === sourceStart && copyDestEnd === destStart) {
|
||||
// contiguous zone => extend copy request
|
||||
copySourceEnd = sourceEnd;
|
||||
copyDestEnd = destEnd;
|
||||
} else {
|
||||
if (copySourceStart !== -1) {
|
||||
// flush existing copy request
|
||||
targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart);
|
||||
}
|
||||
copySourceStart = sourceStart;
|
||||
copySourceEnd = sourceEnd;
|
||||
copyDestStart = destStart;
|
||||
copyDestEnd = destEnd;
|
||||
}
|
||||
|
||||
needed[lineIndex] = false;
|
||||
dest_dy += minimapLineHeight;
|
||||
}
|
||||
|
||||
if (copySourceStart !== -1) {
|
||||
// flush existing copy request
|
||||
targetData.set(lastTargetData.subarray(copySourceStart, copySourceEnd), copyDestStart);
|
||||
}
|
||||
|
||||
return needed;
|
||||
}
|
||||
|
||||
private static _renderLine(
|
||||
target: ImageData,
|
||||
backgroundColor: ParsedColor,
|
||||
renderMinimap: RenderMinimap,
|
||||
colorTracker: MinimapTokensColorTracker,
|
||||
minimapCharRenderer: MinimapCharRenderer,
|
||||
dy: number,
|
||||
tabSize: number,
|
||||
lineData: ViewLineData
|
||||
): void {
|
||||
const content = lineData.content;
|
||||
const tokens = lineData.tokens;
|
||||
const charWidth = (renderMinimap === RenderMinimap.Large ? Constants.x2_CHAR_WIDTH : Constants.x1_CHAR_WIDTH);
|
||||
const maxDx = target.width - charWidth;
|
||||
|
||||
let dx = 0;
|
||||
let charIndex = 0;
|
||||
let tabsCharDelta = 0;
|
||||
|
||||
for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
|
||||
const token = tokens[tokenIndex];
|
||||
const tokenEndIndex = token.endIndex;
|
||||
const tokenColorId = token.getForeground();
|
||||
const tokenColor = colorTracker.getColor(tokenColorId);
|
||||
|
||||
for (; charIndex < tokenEndIndex; charIndex++) {
|
||||
if (dx > maxDx) {
|
||||
// hit edge of minimap
|
||||
return;
|
||||
}
|
||||
const charCode = content.charCodeAt(charIndex);
|
||||
|
||||
if (charCode === CharCode.Tab) {
|
||||
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
|
||||
tabsCharDelta += insertSpacesCount - 1;
|
||||
// No need to render anything since tab is invisible
|
||||
dx += insertSpacesCount * charWidth;
|
||||
} else if (charCode === CharCode.Space) {
|
||||
// No need to render anything since space is invisible
|
||||
dx += charWidth;
|
||||
} else {
|
||||
if (renderMinimap === RenderMinimap.Large) {
|
||||
minimapCharRenderer.x2RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor);
|
||||
} else {
|
||||
minimapCharRenderer.x1RenderChar(target, dx, dy, charCode, tokenColor, backgroundColor);
|
||||
}
|
||||
dx += charWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -139,9 +139,6 @@ export class ViewOverlayWidgets extends ViewPart {
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: IRestrictedRenderingContext): void {
|
||||
|
||||
@@ -11,6 +11,9 @@ import { OverviewRulerImpl } from 'vs/editor/browser/viewParts/overviewRuler/ove
|
||||
import { ViewContext } from 'vs/editor/common/view/viewContext';
|
||||
import { IRenderingContext, IRestrictedRenderingContext } from 'vs/editor/common/view/renderingContext';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { MinimapTokensColorTracker } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
import { ColorId } from 'vs/editor/common/modes';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class DecorationsOverviewRuler extends ViewPart {
|
||||
|
||||
@@ -20,6 +23,9 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
private static _CURSOR_COLOR = 'rgba(0, 0, 102, 0.8)';
|
||||
private static _CURSOR_COLOR_DARK = 'rgba(152, 152, 152, 0.8)';
|
||||
|
||||
private readonly _tokensColorTracker: MinimapTokensColorTracker;
|
||||
private readonly _tokensColorTrackerListener: IDisposable;
|
||||
|
||||
private _overviewRuler: OverviewRulerImpl;
|
||||
|
||||
private _shouldUpdateDecorations: boolean;
|
||||
@@ -33,6 +39,7 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
|
||||
constructor(context: ViewContext, scrollHeight: number, getVerticalOffsetForLine: (lineNumber: number) => number) {
|
||||
super(context);
|
||||
this._tokensColorTracker = MinimapTokensColorTracker.getInstance();
|
||||
this._overviewRuler = new OverviewRulerImpl(
|
||||
1,
|
||||
'decorationsOverviewRuler',
|
||||
@@ -47,6 +54,9 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
let theme = this._context.configuration.editor.viewInfo.theme;
|
||||
this._overviewRuler.setUseDarkColor(!themes.isLightTheme(theme), false);
|
||||
|
||||
this._updateBackground(false);
|
||||
this._tokensColorTrackerListener = this._tokensColorTracker.onDidChange(() => this._updateBackground(true));
|
||||
|
||||
this._shouldUpdateDecorations = true;
|
||||
this._zonesFromDecorations = [];
|
||||
|
||||
@@ -60,6 +70,16 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this._overviewRuler.dispose();
|
||||
this._tokensColorTrackerListener.dispose();
|
||||
}
|
||||
|
||||
private _updateBackground(render: boolean): void {
|
||||
this._overviewRuler.setUseBackground(
|
||||
(this._context.configuration.editor.viewInfo.minimap.enabled
|
||||
? this._tokensColorTracker.getColor(ColorId.DefaultBackground)
|
||||
: null),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
// ---- begin view event handlers
|
||||
@@ -104,6 +124,11 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
shouldRender = true;
|
||||
}
|
||||
|
||||
if (e.viewInfo.minimap) {
|
||||
this._updateBackground(false);
|
||||
shouldRender = true;
|
||||
}
|
||||
|
||||
return shouldRender;
|
||||
}
|
||||
|
||||
@@ -179,9 +204,6 @@ export class DecorationsOverviewRuler extends ViewPart {
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: IRestrictedRenderingContext): void {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { OverviewRulerPosition, OverviewRulerLane, OverviewRulerZone, ColorZone
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as browser from 'vs/base/browser/browser';
|
||||
import { OverviewZoneManager } from 'vs/editor/common/view/overviewZoneManager';
|
||||
import { ParsedColor } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
|
||||
export class OverviewRulerImpl {
|
||||
|
||||
@@ -17,6 +18,7 @@ export class OverviewRulerImpl {
|
||||
private _lanesCount: number;
|
||||
private _zoneManager: OverviewZoneManager;
|
||||
private _canUseTranslate3d: boolean;
|
||||
private _background: ParsedColor;
|
||||
|
||||
private _zoomListener: IDisposable;
|
||||
|
||||
@@ -31,6 +33,7 @@ export class OverviewRulerImpl {
|
||||
this._lanesCount = 3;
|
||||
|
||||
this._canUseTranslate3d = canUseTranslate3d;
|
||||
this._background = null;
|
||||
|
||||
this._zoneManager = new OverviewZoneManager(getVerticalOffsetForLine);
|
||||
this._zoneManager.setMinimumHeight(minimumHeight);
|
||||
@@ -97,6 +100,14 @@ export class OverviewRulerImpl {
|
||||
}
|
||||
}
|
||||
|
||||
public setUseBackground(background: ParsedColor, render: boolean): void {
|
||||
this._background = background;
|
||||
|
||||
if (render) {
|
||||
this.render(true);
|
||||
}
|
||||
}
|
||||
|
||||
public getDomNode(): HTMLCanvasElement {
|
||||
return this._domNode;
|
||||
}
|
||||
@@ -154,7 +165,12 @@ export class OverviewRulerImpl {
|
||||
let id2Color = this._zoneManager.getId2Color();
|
||||
|
||||
let ctx = this._domNode.getContext('2d');
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
if (this._background === null) {
|
||||
ctx.clearRect(0, 0, width, height);
|
||||
} else {
|
||||
ctx.fillStyle = this._background.toCSSHex();
|
||||
ctx.fillRect(0, 0, width, height);
|
||||
}
|
||||
|
||||
if (colorZones.length > 0) {
|
||||
let remainingWidth = width - this._canvasLeftOffset;
|
||||
|
||||
@@ -51,9 +51,6 @@ export class Rulers extends ViewPart {
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: IRestrictedRenderingContext): void {
|
||||
|
||||
@@ -69,9 +69,6 @@ export class ScrollDecorationViewPart extends ViewPart {
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: IRestrictedRenderingContext): void {
|
||||
|
||||
@@ -362,9 +362,6 @@ export class SelectionsOverlay extends DynamicViewOverlay {
|
||||
|
||||
private _previousFrameVisibleRangesWithStyle: LineVisibleRangesWithStyle[][] = [];
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
let output: string[] = [];
|
||||
let visibleStartLineNumber = ctx.visibleRange.startLineNumber;
|
||||
|
||||
@@ -38,7 +38,7 @@ export class ViewCursorRenderData {
|
||||
export class ViewCursor {
|
||||
private readonly _context: ViewContext;
|
||||
private readonly _isSecondary: boolean;
|
||||
private readonly _domNode: FastDomNode;
|
||||
private readonly _domNode: FastDomNode<HTMLElement>;
|
||||
|
||||
private _cursorStyle: TextEditorCursorStyle;
|
||||
private _lineHeight: number;
|
||||
|
||||
@@ -27,7 +27,7 @@ export class ViewCursors extends ViewPart {
|
||||
|
||||
private _isVisible: boolean;
|
||||
|
||||
private _domNode: FastDomNode;
|
||||
private _domNode: FastDomNode<HTMLElement>;
|
||||
|
||||
private _startCursorBlinkAnimation: TimeoutTimer;
|
||||
private _blinkingEnabled: boolean;
|
||||
@@ -297,10 +297,6 @@ export class ViewCursors extends ViewPart {
|
||||
// ---- IViewPart implementation
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
|
||||
this._primaryCursor.prepareRender(ctx);
|
||||
for (let i = 0, len = this._secondaryCursors.length; i < len; i++) {
|
||||
this._secondaryCursors[i].prepareRender(ctx);
|
||||
|
||||
@@ -300,9 +300,6 @@ export class ViewZones extends ViewPart {
|
||||
|
||||
public prepareRender(ctx: IRenderingContext): void {
|
||||
// Nothing to read
|
||||
if (!this.shouldRender()) {
|
||||
throw new Error('I did not ask to render!');
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: IRestrictedRenderingContext): void {
|
||||
|
||||
@@ -30,6 +30,7 @@ import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { InlineDecoration } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { IAddedAction } from 'vs/editor/common/commonCodeEditor';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { ColorId, MetadataConsts, FontStyle } from 'vs/editor/common/modes';
|
||||
|
||||
interface IEditorDiffDecorations {
|
||||
decorations: editorCommon.IModelDeltaDecoration[];
|
||||
@@ -1900,12 +1901,18 @@ class InlineViewZonesComputer extends ViewZonesComputer {
|
||||
|
||||
let actualDecorations = Decoration.filter(decorations, lineNumber, 1, lineContent.length + 1);
|
||||
|
||||
const defaultMetadata = (
|
||||
(FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
|
||||
| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
|
||||
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
|
||||
) >>> 0;
|
||||
|
||||
let r = renderViewLine(new RenderLineInput(
|
||||
(config.fontInfo.isMonospace && !config.viewInfo.disableMonospaceOptimizations),
|
||||
lineContent,
|
||||
originalModel.mightContainRTL(),
|
||||
0,
|
||||
[new ViewLineToken(lineContent.length, '')],
|
||||
[new ViewLineToken(lineContent.length, defaultMetadata)],
|
||||
actualDecorations,
|
||||
tabSize,
|
||||
config.fontInfo.spaceWidth,
|
||||
|
||||
@@ -1006,6 +1006,10 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
|
||||
protected abstract _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void;
|
||||
protected abstract _removeDecorationType(key: string): void;
|
||||
protected abstract _resolveDecorationOptions(typeKey: string, writable: boolean): editorCommon.IModelDecorationOptions;
|
||||
|
||||
public getTelemetryData(): { [key: string]: any; } {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class EditorContextKeysManager extends Disposable {
|
||||
|
||||
@@ -103,13 +103,15 @@ class InternalEditorOptionsHelper {
|
||||
}
|
||||
|
||||
public static createInternalEditorOptions(
|
||||
outerWidth: number, outerHeight: number,
|
||||
outerWidth: number,
|
||||
outerHeight: number,
|
||||
opts: editorCommon.IEditorOptions,
|
||||
fontInfo: FontInfo,
|
||||
editorClassName: string,
|
||||
isDominatedByLongLines: boolean,
|
||||
maxLineNumber: number,
|
||||
canUseTranslate3d: boolean
|
||||
canUseTranslate3d: boolean,
|
||||
pixelRatio: number
|
||||
): editorCommon.InternalEditorOptions {
|
||||
|
||||
let wrappingColumn = toInteger(opts.wrappingColumn, -1);
|
||||
@@ -126,6 +128,7 @@ class InternalEditorOptionsHelper {
|
||||
|
||||
let mouseWheelScrollSensitivity = toFloat(opts.mouseWheelScrollSensitivity, 1);
|
||||
let scrollbar = this._sanitizeScrollbarOpts(opts.scrollbar, mouseWheelScrollSensitivity);
|
||||
let minimap = this._sanitizeMinimapOpts(opts.minimap);
|
||||
|
||||
let glyphMargin = toBoolean(opts.glyphMargin);
|
||||
let lineNumbers = opts.lineNumbers;
|
||||
@@ -177,13 +180,16 @@ class InternalEditorOptionsHelper {
|
||||
lineHeight: fontInfo.lineHeight,
|
||||
showLineNumbers: renderLineNumbers,
|
||||
lineNumbersMinChars: lineNumbersMinChars,
|
||||
lineDecorationsWidth: lineDecorationsWidth,
|
||||
maxDigitWidth: fontInfo.maxDigitWidth,
|
||||
maxLineNumber: maxLineNumber,
|
||||
lineDecorationsWidth: lineDecorationsWidth,
|
||||
typicalHalfwidthCharacterWidth: fontInfo.typicalHalfwidthCharacterWidth,
|
||||
maxDigitWidth: fontInfo.maxDigitWidth,
|
||||
verticalScrollbarWidth: scrollbar.verticalScrollbarSize,
|
||||
horizontalScrollbarHeight: scrollbar.horizontalScrollbarSize,
|
||||
scrollbarArrowSize: scrollbar.arrowSize,
|
||||
verticalScrollbarHasArrows: scrollbar.verticalHasArrows
|
||||
verticalScrollbarHasArrows: scrollbar.verticalHasArrows,
|
||||
minimap: minimap.enabled,
|
||||
pixelRatio: pixelRatio
|
||||
});
|
||||
|
||||
if (isDominatedByLongLines && wrappingColumn > 0) {
|
||||
@@ -196,13 +202,13 @@ class InternalEditorOptionsHelper {
|
||||
// If viewport width wrapping is enabled
|
||||
bareWrappingInfo = {
|
||||
isViewportWrapping: true,
|
||||
wrappingColumn: Math.max(1, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
|
||||
wrappingColumn: Math.max(1, layoutInfo.viewportColumn)
|
||||
};
|
||||
} else if (wrappingColumn > 0 && wordWrap === true) {
|
||||
// Enable smart viewport wrapping
|
||||
bareWrappingInfo = {
|
||||
isViewportWrapping: true,
|
||||
wrappingColumn: Math.min(wrappingColumn, Math.floor((layoutInfo.contentWidth - layoutInfo.verticalScrollbarWidth) / fontInfo.typicalHalfwidthCharacterWidth))
|
||||
wrappingColumn: Math.min(wrappingColumn, layoutInfo.viewportColumn)
|
||||
};
|
||||
} else if (wrappingColumn > 0) {
|
||||
// Wrapping is enabled
|
||||
@@ -276,6 +282,7 @@ class InternalEditorOptionsHelper {
|
||||
renderIndentGuides: toBoolean(opts.renderIndentGuides),
|
||||
renderLineHighlight: renderLineHighlight,
|
||||
scrollbar: scrollbar,
|
||||
minimap: minimap,
|
||||
fixedOverflowWidgets: toBoolean(opts.fixedOverflowWidgets)
|
||||
});
|
||||
|
||||
@@ -353,6 +360,12 @@ class InternalEditorOptionsHelper {
|
||||
mouseWheelScrollSensitivity: mouseWheelScrollSensitivity
|
||||
});
|
||||
}
|
||||
|
||||
private static _sanitizeMinimapOpts(raw: editorCommon.IEditorMinimapOptions): editorCommon.InternalEditorMinimapOptions {
|
||||
return new editorCommon.InternalEditorMinimapOptions({
|
||||
enabled: toBooleanWithDefault(raw.enabled, false)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function toBoolean(value: any): boolean {
|
||||
@@ -516,7 +529,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
|
||||
editorClassName,
|
||||
this._isDominatedByLongLines,
|
||||
this._maxLineNumber,
|
||||
canUseTranslate3d
|
||||
canUseTranslate3d,
|
||||
this._getPixelRatio()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -546,6 +560,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
|
||||
|
||||
protected abstract _getCanUseTranslate3d(): boolean;
|
||||
|
||||
protected abstract _getPixelRatio(): number;
|
||||
|
||||
protected abstract readConfiguration(styling: BareFontInfo): FontInfo;
|
||||
}
|
||||
|
||||
@@ -625,6 +641,11 @@ const editorConfiguration: IConfigurationNode = {
|
||||
'default': DefaultConfig.editor.scrollBeyondLastLine,
|
||||
'description': nls.localize('scrollBeyondLastLine', "Controls if the editor will scroll beyond the last line")
|
||||
},
|
||||
'editor.minimap.enabled': {
|
||||
'type': 'boolean',
|
||||
'default': DefaultConfig.editor.minimap.enabled,
|
||||
'description': nls.localize('minimap.enabled', "Controls if the minimap is shown")
|
||||
},
|
||||
'editor.wrappingColumn': {
|
||||
'type': 'integer',
|
||||
'default': DefaultConfig.editor.wrappingColumn,
|
||||
|
||||
@@ -56,6 +56,9 @@ class ConfigClass implements IConfiguration {
|
||||
verticalHasArrows: false,
|
||||
horizontalHasArrows: false
|
||||
},
|
||||
minimap: {
|
||||
enabled: false
|
||||
},
|
||||
fixedOverflowWidgets: false,
|
||||
overviewRulerLanes: 2,
|
||||
cursorBlinking: 'blink',
|
||||
|
||||
@@ -1063,7 +1063,7 @@ export class Cursor extends EventEmitter {
|
||||
validatedViewPosition = primary.convertModelPositionToViewPosition(validatedPosition.lineNumber, validatedPosition.column);
|
||||
}
|
||||
|
||||
let result = ColumnSelection.columnSelect(primary.config, primary.viewModel, primary.viewState.selection.getStartPosition(), validatedViewPosition.lineNumber, ctx.eventData.mouseColumn - 1);
|
||||
let result = ColumnSelection.columnSelect(primary.config, primary.viewModel, primary.viewState.selection, validatedViewPosition.lineNumber, ctx.eventData.mouseColumn - 1);
|
||||
let selections = result.viewSelections.map(viewSel => primary.convertViewSelectionToModelSelection(viewSel));
|
||||
|
||||
ctx.shouldRevealTarget = (result.reversed ? RevealTarget.TopMost : RevealTarget.BottomMost);
|
||||
|
||||
@@ -66,8 +66,9 @@ export class ColumnSelection {
|
||||
};
|
||||
}
|
||||
|
||||
public static columnSelect(config: CursorConfiguration, model: ICursorSimpleModel, fromViewPosition: Position, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult {
|
||||
let fromViewVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, fromViewPosition);
|
||||
public static columnSelect(config: CursorConfiguration, model: ICursorSimpleModel, fromViewSelection: Selection, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult {
|
||||
const fromViewPosition = new Position(fromViewSelection.selectionStartLineNumber, fromViewSelection.selectionStartColumn);
|
||||
const fromViewVisibleColumn = CursorColumns.visibleColumnFromColumn2(config, model, fromViewPosition);
|
||||
return ColumnSelection._columnSelect(config, model, fromViewPosition.lineNumber, fromViewVisibleColumn, toViewLineNumber, toViewVisualColumn);
|
||||
}
|
||||
|
||||
@@ -76,7 +77,7 @@ export class ColumnSelection {
|
||||
toViewVisualColumn--;
|
||||
}
|
||||
|
||||
return this.columnSelect(config, model, cursor.selection.getStartPosition(), toViewLineNumber, toViewVisualColumn);
|
||||
return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn);
|
||||
}
|
||||
|
||||
public static columnSelectRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult {
|
||||
@@ -93,7 +94,7 @@ export class ColumnSelection {
|
||||
toViewVisualColumn++;
|
||||
}
|
||||
|
||||
return this.columnSelect(config, model, cursor.selection.getStartPosition(), toViewLineNumber, toViewVisualColumn);
|
||||
return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn);
|
||||
}
|
||||
|
||||
public static columnSelectUp(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, isPaged: boolean, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult {
|
||||
@@ -104,7 +105,7 @@ export class ColumnSelection {
|
||||
toViewLineNumber = 1;
|
||||
}
|
||||
|
||||
return this.columnSelect(config, model, cursor.selection.getStartPosition(), toViewLineNumber, toViewVisualColumn);
|
||||
return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn);
|
||||
}
|
||||
|
||||
public static columnSelectDown(config: CursorConfiguration, model: ICursorSimpleModel, cursor: SingleCursorState, isPaged: boolean, toViewLineNumber: number, toViewVisualColumn: number): IColumnSelectResult {
|
||||
@@ -115,6 +116,6 @@ export class ColumnSelection {
|
||||
toViewLineNumber = model.getLineCount();
|
||||
}
|
||||
|
||||
return this.columnSelect(config, model, cursor.selection.getStartPosition(), toViewLineNumber, toViewVisualColumn);
|
||||
return this.columnSelect(config, model, cursor.selection, toViewLineNumber, toViewVisualColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,10 @@ import { KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IClipboardEvent, ICompositionEvent, IKeyboardEventWrapper, ISimpleModel, ITextAreaWrapper, ITypeData, TextAreaState, TextAreaStrategy, createTextAreaState } from 'vs/editor/common/controller/textAreaState';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { EndOfLinePreference } from 'vs/editor/common/editorCommon';
|
||||
|
||||
export const CopyOptions = {
|
||||
forceCopyWithSyntaxHighlighting: false
|
||||
};
|
||||
|
||||
const enum ReadFromTextArea {
|
||||
Type,
|
||||
@@ -324,9 +326,13 @@ export class TextAreaHandler extends Disposable {
|
||||
// ------------- Clipboard operations
|
||||
|
||||
private _ensureClipboardGetsEditorSelection(e: IClipboardEvent): void {
|
||||
let whatToCopy = this._getPlainTextToCopy();
|
||||
let whatToCopy = this.model.getPlainTextToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
|
||||
if (e.canUseTextData()) {
|
||||
e.setTextData(whatToCopy);
|
||||
let whatHTMLToCopy = null;
|
||||
if (!this.Browser.isEdgeOrIE && (whatToCopy.length < 65536 || CopyOptions.forceCopyWithSyntaxHighlighting)) {
|
||||
whatHTMLToCopy = this.model.getHTMLToCopy(this.selections, this.Browser.enableEmptySelectionClipboard);
|
||||
}
|
||||
e.setTextData(whatToCopy, whatHTMLToCopy);
|
||||
} else {
|
||||
this.setTextAreaState('copy or cut', this.textAreaState.fromText(whatToCopy), false);
|
||||
}
|
||||
@@ -344,31 +350,4 @@ export class TextAreaHandler extends Disposable {
|
||||
this.lastCopiedValueIsFromEmptySelection = (selections.length === 1 && selections[0].isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
private _getPlainTextToCopy(): string {
|
||||
let newLineCharacter = this.model.getEOL();
|
||||
let selections = this.selections;
|
||||
|
||||
if (selections.length === 1) {
|
||||
let range: Range = selections[0];
|
||||
if (range.isEmpty()) {
|
||||
if (this.Browser.enableEmptySelectionClipboard) {
|
||||
let modelLineNumber = this.model.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
|
||||
return this.model.getModelLineContent(modelLineNumber) + newLineCharacter;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return this.model.getValueInRange(range, EndOfLinePreference.TextDefined);
|
||||
} else {
|
||||
selections = selections.slice(0).sort(Range.compareRangesUsingStarts);
|
||||
let result: string[] = [];
|
||||
for (let i = 0; i < selections.length; i++) {
|
||||
result.push(this.model.getValueInRange(selections[i], EndOfLinePreference.TextDefined));
|
||||
}
|
||||
|
||||
return result.join(newLineCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import { Constants } from 'vs/editor/common/core/uint';
|
||||
|
||||
export interface IClipboardEvent {
|
||||
canUseTextData(): boolean;
|
||||
setTextData(text: string): void;
|
||||
setTextData(text: string, richText: string): void;
|
||||
getTextData(): string;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ export interface ISimpleModel {
|
||||
getValueInRange(range: Range, eol: EndOfLinePreference): string;
|
||||
getModelLineContent(lineNumber: number): string;
|
||||
getLineCount(): number;
|
||||
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
|
||||
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
|
||||
|
||||
coordinatesConverter: {
|
||||
convertViewPositionToModelPosition(viewPosition: Position): Position;
|
||||
|
||||
@@ -5,30 +5,12 @@
|
||||
'use strict';
|
||||
|
||||
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
import { ViewLineTokenFactory, ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
import { ColorId, FontStyle, StandardTokenType, LanguageId } from 'vs/editor/common/modes';
|
||||
|
||||
const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/;
|
||||
export function toStandardTokenType(tokenType: string): StandardTokenType {
|
||||
let m = tokenType.match(STANDARD_TOKEN_TYPE_REGEXP);
|
||||
if (!m) {
|
||||
return StandardTokenType.Other;
|
||||
}
|
||||
switch (m[1]) {
|
||||
case 'comment':
|
||||
return StandardTokenType.Comment;
|
||||
case 'string':
|
||||
return StandardTokenType.String;
|
||||
case 'regex':
|
||||
return StandardTokenType.RegEx;
|
||||
}
|
||||
throw new Error('Unexpected match for standard token type!');
|
||||
}
|
||||
|
||||
export class LineToken {
|
||||
_lineTokenBrand: void;
|
||||
|
||||
private readonly _colorMap: string[];
|
||||
private readonly _source: LineTokens;
|
||||
private readonly _tokenIndex: number;
|
||||
private readonly _metadata: number;
|
||||
@@ -55,20 +37,11 @@ export class LineToken {
|
||||
return TokenMetadata.getForeground(this._metadata);
|
||||
}
|
||||
|
||||
public get foreground(): string {
|
||||
return this._colorMap[this.foregroundId];
|
||||
}
|
||||
|
||||
public get backgroundId(): ColorId {
|
||||
return TokenMetadata.getBackground(this._metadata);
|
||||
}
|
||||
|
||||
public get background(): string {
|
||||
return this._colorMap[this.backgroundId];
|
||||
}
|
||||
|
||||
constructor(colorMap: string[], source: LineTokens, tokenIndex: number, tokenCount: number, startOffset: number, endOffset: number, metadata: number) {
|
||||
this._colorMap = colorMap;
|
||||
constructor(source: LineTokens, tokenIndex: number, tokenCount: number, startOffset: number, endOffset: number, metadata: number) {
|
||||
this._source = source;
|
||||
this._tokenIndex = tokenIndex;
|
||||
this._metadata = metadata;
|
||||
@@ -100,14 +73,12 @@ export class LineToken {
|
||||
export class LineTokens {
|
||||
_lineTokensBrand: void;
|
||||
|
||||
private readonly _colorMap: string[];
|
||||
private readonly _tokens: Uint32Array;
|
||||
private readonly _tokensCount: number;
|
||||
private readonly _text: string;
|
||||
private readonly _textLength: number;
|
||||
|
||||
constructor(colorMap: string[], tokens: Uint32Array, text: string) {
|
||||
this._colorMap = colorMap;
|
||||
constructor(tokens: Uint32Array, text: string) {
|
||||
this._tokens = tokens;
|
||||
this._tokensCount = (this._tokens.length >>> 1);
|
||||
this._text = text;
|
||||
@@ -159,7 +130,7 @@ export class LineTokens {
|
||||
* @return The index of the token containing the offset.
|
||||
*/
|
||||
public findTokenIndexAtOffset(offset: number): number {
|
||||
return TokenMetadata.findIndexInSegmentsArray(this._tokens, offset);
|
||||
return ViewLineTokenFactory.findIndexInSegmentsArray(this._tokens, offset);
|
||||
}
|
||||
|
||||
public findTokenAtOffset(offset: number): LineToken {
|
||||
@@ -176,7 +147,7 @@ export class LineTokens {
|
||||
endOffset = this._textLength;
|
||||
}
|
||||
let metadata = this._tokens[(tokenIndex << 1) + 1];
|
||||
return new LineToken(this._colorMap, this, tokenIndex, this._tokensCount, startOffset, endOffset, metadata);
|
||||
return new LineToken(this, tokenIndex, this._tokensCount, startOffset, endOffset, metadata);
|
||||
}
|
||||
|
||||
public firstToken(): LineToken {
|
||||
@@ -194,10 +165,10 @@ export class LineTokens {
|
||||
}
|
||||
|
||||
public inflate(): ViewLineToken[] {
|
||||
return TokenMetadata.inflateArr(this._tokens, this._textLength);
|
||||
return ViewLineTokenFactory.inflateArr(this._tokens, this._textLength);
|
||||
}
|
||||
|
||||
public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): ViewLineToken[] {
|
||||
return TokenMetadata.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
|
||||
return ViewLineTokenFactory.sliceAndInflate(this._tokens, startOffset, endOffset, deltaOffset, this._textLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ColorId } from 'vs/editor/common/modes';
|
||||
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
|
||||
|
||||
/**
|
||||
* A token on a line.
|
||||
*/
|
||||
@@ -14,17 +17,25 @@ export class ViewLineToken {
|
||||
* last char index of this token (not inclusive).
|
||||
*/
|
||||
public readonly endIndex: number;
|
||||
public readonly type: string;
|
||||
private readonly _metadata: number;
|
||||
|
||||
constructor(endIndex: number, type: string) {
|
||||
constructor(endIndex: number, metadata: number) {
|
||||
this.endIndex = endIndex;
|
||||
this.type = type;
|
||||
this._metadata = metadata;
|
||||
}
|
||||
|
||||
public getForeground(): ColorId {
|
||||
return TokenMetadata.getForeground(this._metadata);
|
||||
}
|
||||
|
||||
public getType(): string {
|
||||
return TokenMetadata.getClassNameFromMetadata(this._metadata);
|
||||
}
|
||||
|
||||
private static _equals(a: ViewLineToken, b: ViewLineToken): boolean {
|
||||
return (
|
||||
a.endIndex === b.endIndex
|
||||
&& a.type === b.type
|
||||
&& a._metadata === b._metadata
|
||||
);
|
||||
}
|
||||
|
||||
@@ -42,3 +53,62 @@ export class ViewLineToken {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewLineTokenFactory {
|
||||
|
||||
public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] {
|
||||
let result: ViewLineToken[] = [];
|
||||
|
||||
for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
|
||||
let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
|
||||
let metadata = tokens[(i << 1) + 1];
|
||||
|
||||
result[i] = new ViewLineToken(endOffset, metadata);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] {
|
||||
const tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
|
||||
const maxEndOffset = (endOffset - startOffset + deltaOffset);
|
||||
let result: ViewLineToken[] = [], resultLen = 0;
|
||||
|
||||
for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
|
||||
let tokenStartOffset = tokens[(i << 1)];
|
||||
|
||||
if (tokenStartOffset >= endOffset) {
|
||||
break;
|
||||
}
|
||||
|
||||
let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
|
||||
let newEndOffset = Math.min(maxEndOffset, tokenEndOffset - startOffset + deltaOffset);
|
||||
let metadata = tokens[(i << 1) + 1];
|
||||
|
||||
result[resultLen++] = new ViewLineToken(newEndOffset, metadata);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static findIndexInSegmentsArray(tokens: Uint32Array, desiredIndex: number): number {
|
||||
|
||||
let low = 0;
|
||||
let high = (tokens.length >>> 1) - 1;
|
||||
|
||||
while (low < high) {
|
||||
|
||||
let mid = low + Math.ceil((high - low) / 2);
|
||||
|
||||
let value = tokens[(mid << 1)];
|
||||
|
||||
if (value > desiredIndex) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,6 +153,18 @@ export interface IEditorScrollbarOptions {
|
||||
horizontalSliderSize?: number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configuration options for editor minimap
|
||||
*/
|
||||
export interface IEditorMinimapOptions {
|
||||
/**
|
||||
* Enable the rendering of the minimap.
|
||||
* Defaults to false.
|
||||
*/
|
||||
enabled?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes how to indent wrapped lines.
|
||||
*/
|
||||
@@ -257,6 +269,10 @@ export interface IEditorOptions {
|
||||
* Control the behavior and rendering of the scrollbars.
|
||||
*/
|
||||
scrollbar?: IEditorScrollbarOptions;
|
||||
/**
|
||||
* Control the behavior and rendering of the minimap.
|
||||
*/
|
||||
minimap?: IEditorMinimapOptions;
|
||||
/**
|
||||
* Display overflow widgets as `fixed`.
|
||||
* Defaults to `false`.
|
||||
@@ -612,6 +628,37 @@ export class InternalEditorScrollbarOptions {
|
||||
}
|
||||
}
|
||||
|
||||
export class InternalEditorMinimapOptions {
|
||||
readonly _internalEditorMinimapOptionsBrand: void;
|
||||
|
||||
readonly enabled: boolean;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
constructor(source: {
|
||||
enabled: boolean;
|
||||
}) {
|
||||
this.enabled = Boolean(source.enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public equals(other: InternalEditorMinimapOptions): boolean {
|
||||
return (
|
||||
this.enabled === other.enabled
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public clone(): InternalEditorMinimapOptions {
|
||||
return new InternalEditorMinimapOptions(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorWrappingInfo {
|
||||
readonly _editorWrappingInfoBrand: void;
|
||||
|
||||
@@ -692,6 +739,7 @@ export class InternalEditorViewOptions {
|
||||
readonly renderIndentGuides: boolean;
|
||||
readonly renderLineHighlight: 'none' | 'gutter' | 'line' | 'all';
|
||||
readonly scrollbar: InternalEditorScrollbarOptions;
|
||||
readonly minimap: InternalEditorMinimapOptions;
|
||||
readonly fixedOverflowWidgets: boolean;
|
||||
|
||||
/**
|
||||
@@ -724,6 +772,7 @@ export class InternalEditorViewOptions {
|
||||
renderIndentGuides: boolean;
|
||||
renderLineHighlight: 'none' | 'gutter' | 'line' | 'all';
|
||||
scrollbar: InternalEditorScrollbarOptions;
|
||||
minimap: InternalEditorMinimapOptions;
|
||||
fixedOverflowWidgets: boolean;
|
||||
}) {
|
||||
this.theme = String(source.theme);
|
||||
@@ -752,6 +801,7 @@ export class InternalEditorViewOptions {
|
||||
this.renderIndentGuides = Boolean(source.renderIndentGuides);
|
||||
this.renderLineHighlight = source.renderLineHighlight;
|
||||
this.scrollbar = source.scrollbar.clone();
|
||||
this.minimap = source.minimap.clone();
|
||||
this.fixedOverflowWidgets = Boolean(source.fixedOverflowWidgets);
|
||||
}
|
||||
|
||||
@@ -814,6 +864,7 @@ export class InternalEditorViewOptions {
|
||||
&& this.renderIndentGuides === other.renderIndentGuides
|
||||
&& this.renderLineHighlight === other.renderLineHighlight
|
||||
&& this.scrollbar.equals(other.scrollbar)
|
||||
&& this.minimap.equals(other.minimap)
|
||||
&& this.fixedOverflowWidgets === other.fixedOverflowWidgets
|
||||
);
|
||||
}
|
||||
@@ -849,6 +900,7 @@ export class InternalEditorViewOptions {
|
||||
renderIndentGuides: this.renderIndentGuides !== newOpts.renderIndentGuides,
|
||||
renderLineHighlight: this.renderLineHighlight !== newOpts.renderLineHighlight,
|
||||
scrollbar: (!this.scrollbar.equals(newOpts.scrollbar)),
|
||||
minimap: (!this.minimap.equals(newOpts.minimap)),
|
||||
fixedOverflowWidgets: this.fixedOverflowWidgets !== newOpts.fixedOverflowWidgets
|
||||
};
|
||||
}
|
||||
@@ -888,6 +940,7 @@ export interface IViewConfigurationChangedEvent {
|
||||
readonly renderIndentGuides: boolean;
|
||||
readonly renderLineHighlight: boolean;
|
||||
readonly scrollbar: boolean;
|
||||
readonly minimap: boolean;
|
||||
readonly fixedOverflowWidgets: boolean;
|
||||
}
|
||||
|
||||
@@ -2743,6 +2796,12 @@ export class OverviewRulerPosition {
|
||||
}
|
||||
}
|
||||
|
||||
export enum RenderMinimap {
|
||||
None = 0,
|
||||
Small = 1,
|
||||
Large = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal layout details of the editor.
|
||||
*/
|
||||
@@ -2810,6 +2869,21 @@ export class EditorLayoutInfo {
|
||||
*/
|
||||
readonly contentHeight: number;
|
||||
|
||||
/**
|
||||
* The width of the minimap
|
||||
*/
|
||||
readonly minimapWidth: number;
|
||||
|
||||
/**
|
||||
* Minimap render type
|
||||
*/
|
||||
readonly renderMinimap: RenderMinimap;
|
||||
|
||||
/**
|
||||
* The number of columns (of typical characters) fitting on a viewport line.
|
||||
*/
|
||||
readonly viewportColumn: number;
|
||||
|
||||
/**
|
||||
* The width of the vertical scrollbar.
|
||||
*/
|
||||
@@ -2842,6 +2916,9 @@ export class EditorLayoutInfo {
|
||||
contentLeft: number;
|
||||
contentWidth: number;
|
||||
contentHeight: number;
|
||||
renderMinimap: RenderMinimap;
|
||||
minimapWidth: number;
|
||||
viewportColumn: number;
|
||||
verticalScrollbarWidth: number;
|
||||
horizontalScrollbarHeight: number;
|
||||
overviewRuler: OverviewRulerPosition;
|
||||
@@ -2860,6 +2937,9 @@ export class EditorLayoutInfo {
|
||||
this.contentLeft = source.contentLeft | 0;
|
||||
this.contentWidth = source.contentWidth | 0;
|
||||
this.contentHeight = source.contentHeight | 0;
|
||||
this.renderMinimap = source.renderMinimap | 0;
|
||||
this.minimapWidth = source.minimapWidth | 0;
|
||||
this.viewportColumn = source.viewportColumn | 0;
|
||||
this.verticalScrollbarWidth = source.verticalScrollbarWidth | 0;
|
||||
this.horizontalScrollbarHeight = source.horizontalScrollbarHeight | 0;
|
||||
this.overviewRuler = source.overviewRuler.clone();
|
||||
@@ -2884,6 +2964,9 @@ export class EditorLayoutInfo {
|
||||
&& this.contentLeft === other.contentLeft
|
||||
&& this.contentWidth === other.contentWidth
|
||||
&& this.contentHeight === other.contentHeight
|
||||
&& this.renderMinimap === other.renderMinimap
|
||||
&& this.minimapWidth === other.minimapWidth
|
||||
&& this.viewportColumn === other.viewportColumn
|
||||
&& this.verticalScrollbarWidth === other.verticalScrollbarWidth
|
||||
&& this.horizontalScrollbarHeight === other.horizontalScrollbarHeight
|
||||
&& this.overviewRuler.equals(other.overviewRuler)
|
||||
@@ -4039,6 +4122,11 @@ export interface ICommonCodeEditor extends IEditor {
|
||||
* Get the layout info for the editor.
|
||||
*/
|
||||
getLayoutInfo(): EditorLayoutInfo;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
getTelemetryData(): { [key: string]: any; };
|
||||
}
|
||||
|
||||
export interface ICommonDiffEditor extends IEditor {
|
||||
|
||||
@@ -62,12 +62,12 @@ export abstract class EditorAction extends ConfigEditorCommand {
|
||||
}
|
||||
|
||||
public runEditorCommand(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise<void> {
|
||||
this.reportTelemetry(accessor);
|
||||
this.reportTelemetry(accessor, editor);
|
||||
return this.run(accessor, editor, args);
|
||||
}
|
||||
|
||||
protected reportTelemetry(accessor: ServicesAccessor) {
|
||||
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id });
|
||||
protected reportTelemetry(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor) {
|
||||
accessor.get(ITelemetryService).publicLog('editorActionInvoked', { name: this.label, id: this.id, ...editor.getTelemetryData() });
|
||||
}
|
||||
|
||||
public abstract run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void | TPromise<void>;
|
||||
|
||||
@@ -258,16 +258,16 @@ export class ModelLine {
|
||||
this._lineTokens = tokens.buffer;
|
||||
}
|
||||
|
||||
public getTokens(topLevelLanguageId: LanguageId, colorMap: string[]): LineTokens {
|
||||
public getTokens(topLevelLanguageId: LanguageId): LineTokens {
|
||||
let rawLineTokens = this._lineTokens;
|
||||
if (rawLineTokens) {
|
||||
return new LineTokens(colorMap, new Uint32Array(rawLineTokens), this._text);
|
||||
return new LineTokens(new Uint32Array(rawLineTokens), this._text);
|
||||
}
|
||||
|
||||
let lineTokens = new Uint32Array(2);
|
||||
lineTokens[0] = 0;
|
||||
lineTokens[1] = ModelLine._getDefaultMetadata(topLevelLanguageId);
|
||||
return new LineTokens(colorMap, lineTokens, this._text);
|
||||
return new LineTokens(lineTokens, this._text);
|
||||
}
|
||||
|
||||
// --- END TOKENS
|
||||
|
||||
@@ -63,7 +63,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
|
||||
|
||||
private _languageIdentifier: LanguageIdentifier;
|
||||
private _tokenizationListener: IDisposable;
|
||||
private _colorMap: string[];
|
||||
private _tokenizationSupport: ITokenizationSupport;
|
||||
|
||||
private _invalidLineStartIndex: number;
|
||||
@@ -78,7 +77,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
|
||||
|
||||
this._languageIdentifier = languageIdentifier || NULL_LANGUAGE_IDENTIFIER;
|
||||
this._tokenizationListener = TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.languages.indexOf(this._languageIdentifier.language) === -1) {
|
||||
if (e.changedLanguages.indexOf(this._languageIdentifier.language) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -140,7 +139,6 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
|
||||
}
|
||||
}
|
||||
|
||||
this._colorMap = TokenizationRegistry.getColorMap();
|
||||
this._lastState = null;
|
||||
this._invalidLineStartIndex = 0;
|
||||
this._beginBackgroundTokenization();
|
||||
@@ -183,7 +181,7 @@ export class TextModelWithTokens extends TextModel implements editorCommon.IToke
|
||||
}
|
||||
|
||||
private _getLineTokens(lineNumber: number): LineTokens {
|
||||
return this._lines[lineNumber - 1].getTokens(this._languageIdentifier.id, this._colorMap);
|
||||
return this._lines[lineNumber - 1].getTokens(this._languageIdentifier.id);
|
||||
}
|
||||
|
||||
public getLanguageIdentifier(): LanguageIdentifier {
|
||||
|
||||
@@ -4,35 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
import { ColorId, FontStyle, StandardTokenType, MetadataConsts, LanguageId } from 'vs/editor/common/modes';
|
||||
|
||||
export class TokenMetadata {
|
||||
|
||||
public static toBinaryStr(metadata: number): string {
|
||||
let r = metadata.toString(2);
|
||||
while (r.length < 32) {
|
||||
r = '0' + r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public static printMetadata(metadata: number): void {
|
||||
let languageId = TokenMetadata.getLanguageId(metadata);
|
||||
let tokenType = TokenMetadata.getTokenType(metadata);
|
||||
let fontStyle = TokenMetadata.getFontStyle(metadata);
|
||||
let foreground = TokenMetadata.getForeground(metadata);
|
||||
let background = TokenMetadata.getBackground(metadata);
|
||||
|
||||
console.log({
|
||||
languageId: languageId,
|
||||
tokenType: tokenType,
|
||||
fontStyle: fontStyle,
|
||||
foreground: foreground,
|
||||
background: background,
|
||||
});
|
||||
}
|
||||
|
||||
public static getLanguageId(metadata: number): LanguageId {
|
||||
return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET;
|
||||
}
|
||||
@@ -53,7 +28,7 @@ export class TokenMetadata {
|
||||
return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET;
|
||||
}
|
||||
|
||||
private static _getClassNameFromMetadata(metadata: number): string {
|
||||
public static getClassNameFromMetadata(metadata: number): string {
|
||||
let foreground = this.getForeground(metadata);
|
||||
let className = 'mtk' + foreground;
|
||||
|
||||
@@ -70,59 +45,4 @@ export class TokenMetadata {
|
||||
|
||||
return className;
|
||||
}
|
||||
|
||||
public static inflateArr(tokens: Uint32Array, lineLength: number): ViewLineToken[] {
|
||||
let result: ViewLineToken[] = [];
|
||||
|
||||
for (let i = 0, len = (tokens.length >>> 1); i < len; i++) {
|
||||
let endOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
|
||||
let metadata = tokens[(i << 1) + 1];
|
||||
|
||||
result[i] = new ViewLineToken(endOffset, this._getClassNameFromMetadata(metadata));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static sliceAndInflate(tokens: Uint32Array, startOffset: number, endOffset: number, deltaOffset: number, lineLength: number): ViewLineToken[] {
|
||||
let tokenIndex = this.findIndexInSegmentsArray(tokens, startOffset);
|
||||
let result: ViewLineToken[] = [], resultLen = 0;
|
||||
|
||||
for (let i = tokenIndex, len = (tokens.length >>> 1); i < len; i++) {
|
||||
let tokenStartOffset = tokens[(i << 1)];
|
||||
|
||||
if (tokenStartOffset >= endOffset) {
|
||||
break;
|
||||
}
|
||||
|
||||
let tokenEndOffset = (i + 1 < len ? tokens[((i + 1) << 1)] : lineLength);
|
||||
let newEndOffset = tokenEndOffset - startOffset + deltaOffset;
|
||||
let metadata = tokens[(i << 1) + 1];
|
||||
|
||||
result[resultLen++] = new ViewLineToken(newEndOffset, this._getClassNameFromMetadata(metadata));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static findIndexInSegmentsArray(tokens: Uint32Array, desiredIndex: number): number {
|
||||
|
||||
let low = 0;
|
||||
let high = (tokens.length >>> 1) - 1;
|
||||
|
||||
while (low < high) {
|
||||
|
||||
let mid = low + Math.ceil((high - low) / 2);
|
||||
|
||||
let value = tokens[(mid << 1)];
|
||||
|
||||
if (value > desiredIndex) {
|
||||
high = mid - 1;
|
||||
} else {
|
||||
low = mid;
|
||||
}
|
||||
}
|
||||
|
||||
return low;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ import LanguageFeatureRegistry from 'vs/editor/common/modes/languageFeatureRegis
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import Event from 'vs/base/common/event';
|
||||
import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
/**
|
||||
* Open ended enum at runtime
|
||||
@@ -801,60 +803,45 @@ export const LinkProviderRegistry = new LanguageFeatureRegistry<LinkProvider>();
|
||||
* @internal
|
||||
*/
|
||||
export interface ITokenizationSupportChangedEvent {
|
||||
languages: string[];
|
||||
changedLanguages: string[];
|
||||
changedColorMap: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class TokenizationRegistryImpl {
|
||||
export interface ITokenizationRegistry {
|
||||
|
||||
private _map: { [language: string]: ITokenizationSupport };
|
||||
|
||||
private _onDidChange: Emitter<ITokenizationSupportChangedEvent> = new Emitter<ITokenizationSupportChangedEvent>();
|
||||
public onDidChange: Event<ITokenizationSupportChangedEvent> = this._onDidChange.event;
|
||||
|
||||
private _colorMap: string[];
|
||||
|
||||
constructor() {
|
||||
this._map = Object.create(null);
|
||||
this._colorMap = null;
|
||||
}
|
||||
/**
|
||||
* An event triggered when:
|
||||
* - a tokenization support is registered, unregistered or changed.
|
||||
* - the color map is changed.
|
||||
*/
|
||||
onDidChange: Event<ITokenizationSupportChangedEvent>;
|
||||
|
||||
/**
|
||||
* Fire a change event for a language.
|
||||
* This is useful for languages that embed other languages.
|
||||
*/
|
||||
public fire(languages: string[]): void {
|
||||
this._onDidChange.fire({ languages: languages });
|
||||
}
|
||||
fire(languages: string[]): void;
|
||||
|
||||
public register(language: string, support: ITokenizationSupport): IDisposable {
|
||||
this._map[language] = support;
|
||||
this.fire([language]);
|
||||
return {
|
||||
dispose: () => {
|
||||
if (this._map[language] !== support) {
|
||||
return;
|
||||
}
|
||||
delete this._map[language];
|
||||
this.fire([language]);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Register a tokenization support.
|
||||
*/
|
||||
register(language: string, support: ITokenizationSupport): IDisposable;
|
||||
|
||||
public get(language: string): ITokenizationSupport {
|
||||
return (this._map[language] || null);
|
||||
}
|
||||
/**
|
||||
* Get the tokenization support for a language.
|
||||
* Returns null if not found.
|
||||
*/
|
||||
get(language: string): ITokenizationSupport;
|
||||
|
||||
public setColorMap(colorMap: string[]): void {
|
||||
this._colorMap = colorMap;
|
||||
this.fire(Object.keys(this._map));
|
||||
}
|
||||
/**
|
||||
* Set the new color map that all tokens will use in their ColorId binary encoded bits for foreground and background.
|
||||
*/
|
||||
setColorMap(colorMap: Color[]): void;
|
||||
|
||||
public getColorMap(): string[] {
|
||||
return this._colorMap;
|
||||
}
|
||||
getColorMap(): Color[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -398,8 +398,8 @@ export class MonarchTokenizer implements modes.ITokenizationSupport {
|
||||
return;
|
||||
}
|
||||
let isOneOfMyEmbeddedModes = false;
|
||||
for (let i = 0, len = e.languages.length; i < len; i++) {
|
||||
let language = e.languages[i];
|
||||
for (let i = 0, len = e.changedLanguages.length; i < len; i++) {
|
||||
let language = e.changedLanguages[i];
|
||||
if (this._embeddedModes[language]) {
|
||||
isOneOfMyEmbeddedModes = true;
|
||||
break;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ColorId, FontStyle, MetadataConsts, LanguageId } from 'vs/editor/common/modes';
|
||||
import { toStandardTokenType } from 'vs/editor/common/core/lineTokens';
|
||||
import { ColorId, FontStyle, MetadataConsts, LanguageId, StandardTokenType } from 'vs/editor/common/modes';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export interface IThemeRule {
|
||||
token: string;
|
||||
@@ -142,7 +142,7 @@ function resolveParsedThemeRules(parsedThemeRules: ParsedThemeRule[]): Theme {
|
||||
export class ColorMap {
|
||||
|
||||
private _lastColorId: number;
|
||||
private _id2color: string[];
|
||||
private _id2color: Color[];
|
||||
private _color2id: Map<string, ColorId>;
|
||||
|
||||
constructor() {
|
||||
@@ -165,11 +165,11 @@ export class ColorMap {
|
||||
}
|
||||
value = ++this._lastColorId;
|
||||
this._color2id.set(color, value);
|
||||
this._id2color[value] = color;
|
||||
this._id2color[value] = Color.fromHex('#' + color);
|
||||
return value;
|
||||
}
|
||||
|
||||
public getColorMap(): string[] {
|
||||
public getColorMap(): Color[] {
|
||||
return this._id2color.slice(0);
|
||||
}
|
||||
|
||||
@@ -187,15 +187,15 @@ export class Theme {
|
||||
|
||||
private readonly _colorMap: ColorMap;
|
||||
private readonly _root: ThemeTrieElement;
|
||||
private readonly _cache: Map<string, ThemeTrieElementRule>;
|
||||
private readonly _cache: Map<string, number>;
|
||||
|
||||
constructor(colorMap: ColorMap, root: ThemeTrieElement) {
|
||||
this._colorMap = colorMap;
|
||||
this._root = root;
|
||||
this._cache = new Map<string, ThemeTrieElementRule>();
|
||||
this._cache = new Map<string, number>();
|
||||
}
|
||||
|
||||
public getColorMap(): string[] {
|
||||
public getColorMap(): Color[] {
|
||||
return this._colorMap.getColorMap();
|
||||
}
|
||||
|
||||
@@ -207,26 +207,46 @@ export class Theme {
|
||||
}
|
||||
|
||||
public _match(token: string): ThemeTrieElementRule {
|
||||
let result = this._cache.get(token);
|
||||
if (typeof result === 'undefined') {
|
||||
result = this._root.match(token);
|
||||
this._cache.set(token, result);
|
||||
}
|
||||
return result;
|
||||
return this._root.match(token);
|
||||
}
|
||||
|
||||
public match(languageId: LanguageId, token: string): number {
|
||||
let rule = this._match(token);
|
||||
let standardToken = toStandardTokenType(token);
|
||||
// The cache contains the metadata without the language bits set.
|
||||
let result = this._cache.get(token);
|
||||
if (typeof result === 'undefined') {
|
||||
let rule = this._match(token);
|
||||
let standardToken = toStandardTokenType(token);
|
||||
result = (
|
||||
rule.metadata
|
||||
| (standardToken << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
) >>> 0;
|
||||
this._cache.set(token, result);
|
||||
}
|
||||
|
||||
return (
|
||||
rule.metadata
|
||||
| (standardToken << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
result
|
||||
| (languageId << MetadataConsts.LANGUAGEID_OFFSET)
|
||||
) >>> 0;
|
||||
}
|
||||
}
|
||||
|
||||
const STANDARD_TOKEN_TYPE_REGEXP = /\b(comment|string|regex)\b/;
|
||||
export function toStandardTokenType(tokenType: string): StandardTokenType {
|
||||
let m = tokenType.match(STANDARD_TOKEN_TYPE_REGEXP);
|
||||
if (!m) {
|
||||
return StandardTokenType.Other;
|
||||
}
|
||||
switch (m[1]) {
|
||||
case 'comment':
|
||||
return StandardTokenType.Comment;
|
||||
case 'string':
|
||||
return StandardTokenType.String;
|
||||
case 'regex':
|
||||
return StandardTokenType.RegEx;
|
||||
}
|
||||
throw new Error('Unexpected match for standard token type!');
|
||||
}
|
||||
|
||||
export function strcmp(a: string, b: string): number {
|
||||
if (a < b) {
|
||||
return -1;
|
||||
@@ -370,3 +390,15 @@ export class ThemeTrieElement {
|
||||
child.insert(tail, fontStyle, foreground, background);
|
||||
}
|
||||
}
|
||||
|
||||
export function generateTokensCSSForColorMap(colorMap: Color[]): string {
|
||||
let rules: string[] = [];
|
||||
for (let i = 1, len = colorMap.length; i < len; i++) {
|
||||
let color = colorMap[i];
|
||||
rules[i] = `.mtk${i} { color: ${color.toString()}; }`;
|
||||
}
|
||||
rules.push('.mtki { font-style: italic; }');
|
||||
rules.push('.mtkb { font-weight: bold; }');
|
||||
rules.push('.mtku { text-decoration: underline; }');
|
||||
return rules.join('\n');
|
||||
}
|
||||
|
||||
@@ -8,11 +8,101 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { IState, ITokenizationSupport, TokenizationRegistry, LanguageId } from 'vs/editor/common/modes';
|
||||
import { NULL_STATE, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
|
||||
export function tokenizeToString(text: string, languageId: string): string {
|
||||
return _tokenizeToString(text, _getSafeTokenizationSupport(languageId));
|
||||
}
|
||||
|
||||
export function tokenizeLineToHTML(text: string, viewLineTokens: ViewLineToken[], rules: { [key: string]: string }, options: { startOffset: number, endOffset: number, tabSize: number }): string {
|
||||
let tabSize = options.tabSize;
|
||||
let result = `<div>`;
|
||||
let charIndex = options.startOffset;
|
||||
let tabsCharDelta = 0;
|
||||
|
||||
for (let tokenIndex = 0, lenJ = viewLineTokens.length; tokenIndex < lenJ; tokenIndex++) {
|
||||
const token = viewLineTokens[tokenIndex];
|
||||
const tokenEndIndex = token.endIndex;
|
||||
|
||||
if (token.endIndex < options.startOffset) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const tokenType = token.getType();
|
||||
let partContentCnt = 0;
|
||||
let partContent = '';
|
||||
|
||||
for (; charIndex < tokenEndIndex && charIndex < options.endOffset; charIndex++) {
|
||||
const charCode = text.charCodeAt(charIndex);
|
||||
|
||||
switch (charCode) {
|
||||
case CharCode.Tab:
|
||||
let insertSpacesCount = tabSize - (charIndex + tabsCharDelta) % tabSize;
|
||||
tabsCharDelta += insertSpacesCount - 1;
|
||||
while (insertSpacesCount > 0) {
|
||||
partContent += ' ';
|
||||
partContentCnt++;
|
||||
insertSpacesCount--;
|
||||
}
|
||||
break;
|
||||
|
||||
case CharCode.Space:
|
||||
partContent += ' ';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.LessThan:
|
||||
partContent += '<';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.GreaterThan:
|
||||
partContent += '>';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.Ampersand:
|
||||
partContent += '&';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.Null:
|
||||
partContent += '�';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.UTF8_BOM:
|
||||
case CharCode.LINE_SEPARATOR_2028:
|
||||
partContent += '\ufffd';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
case CharCode.CarriageReturn:
|
||||
// zero width space, because carriage return would introduce a line break
|
||||
partContent += '​';
|
||||
partContentCnt++;
|
||||
break;
|
||||
|
||||
default:
|
||||
partContent += String.fromCharCode(charCode);
|
||||
partContentCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: adopt new view line tokens.
|
||||
let style = tokenType.split(' ').map(type => rules[type]).join('');
|
||||
result += `<span style="${style}">${partContent}</span>`;
|
||||
|
||||
if (token.endIndex > options.endOffset) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result += `</div>`;
|
||||
return result;
|
||||
}
|
||||
|
||||
function _getSafeTokenizationSupport(languageId: string): ITokenizationSupport {
|
||||
let tokenizationSupport = TokenizationRegistry.get(languageId);
|
||||
if (tokenizationSupport) {
|
||||
@@ -37,13 +127,13 @@ function _tokenizeToString(text: string, tokenizationSupport: ITokenizationSuppo
|
||||
}
|
||||
|
||||
let tokenizationResult = tokenizationSupport.tokenize2(line, currentState, 0);
|
||||
let lineTokens = new LineTokens(null, tokenizationResult.tokens, line);
|
||||
let lineTokens = new LineTokens(tokenizationResult.tokens, line);
|
||||
let viewLineTokens = lineTokens.inflate();
|
||||
|
||||
let startOffset = 0;
|
||||
for (let j = 0, lenJ = viewLineTokens.length; j < lenJ; j++) {
|
||||
const viewLineToken = viewLineTokens[j];
|
||||
result += `<span class="${viewLineToken.type}">${strings.escape(line.substring(startOffset, viewLineToken.endIndex))}</span>`;
|
||||
result += `<span class="${viewLineToken.getType()}">${strings.escape(line.substring(startOffset, viewLineToken.endIndex))}</span>`;
|
||||
startOffset = viewLineToken.endIndex;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { ITokenizationRegistry, ITokenizationSupport, ITokenizationSupportChangedEvent } from 'vs/editor/common/modes';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export class TokenizationRegistryImpl implements ITokenizationRegistry {
|
||||
|
||||
private _map: { [language: string]: ITokenizationSupport };
|
||||
|
||||
private _onDidChange: Emitter<ITokenizationSupportChangedEvent> = new Emitter<ITokenizationSupportChangedEvent>();
|
||||
public onDidChange: Event<ITokenizationSupportChangedEvent> = this._onDidChange.event;
|
||||
|
||||
private _colorMap: Color[];
|
||||
|
||||
constructor() {
|
||||
this._map = Object.create(null);
|
||||
this._colorMap = null;
|
||||
}
|
||||
|
||||
public fire(languages: string[]): void {
|
||||
this._onDidChange.fire({
|
||||
changedLanguages: languages,
|
||||
changedColorMap: false
|
||||
});
|
||||
}
|
||||
|
||||
public register(language: string, support: ITokenizationSupport): IDisposable {
|
||||
this._map[language] = support;
|
||||
this.fire([language]);
|
||||
return {
|
||||
dispose: () => {
|
||||
if (this._map[language] !== support) {
|
||||
return;
|
||||
}
|
||||
delete this._map[language];
|
||||
this.fire([language]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public get(language: string): ITokenizationSupport {
|
||||
return (this._map[language] || null);
|
||||
}
|
||||
|
||||
public setColorMap(colorMap: Color[]): void {
|
||||
this._colorMap = colorMap;
|
||||
this._onDidChange.fire({
|
||||
changedLanguages: Object.keys(this._map),
|
||||
changedColorMap: true
|
||||
});
|
||||
}
|
||||
|
||||
public getColorMap(): Color[] {
|
||||
return this._colorMap;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import { IThemeRule } from 'vs/editor/common/modes/supports/tokenization';
|
||||
|
||||
/* -------------------------------- Begin vs tokens -------------------------------- */
|
||||
export const vs: IThemeRule[] = [
|
||||
{ token: '', foreground: '000000' },
|
||||
{ token: '', foreground: '000000', background: 'fffffe' },
|
||||
{ token: 'invalid', foreground: 'cd3131' },
|
||||
{ token: 'emphasis', fontStyle: 'italic' },
|
||||
{ token: 'strong', fontStyle: 'bold' },
|
||||
@@ -70,7 +70,7 @@ export const vs: IThemeRule[] = [
|
||||
|
||||
/* -------------------------------- Begin vs-dark tokens -------------------------------- */
|
||||
export const vs_dark: IThemeRule[] = [
|
||||
{ token: '', foreground: 'D4D4D4' },
|
||||
{ token: '', foreground: 'D4D4D4', background: '1E1E1E' },
|
||||
{ token: 'invalid', foreground: 'f44747' },
|
||||
{ token: 'emphasis', fontStyle: 'italic' },
|
||||
{ token: 'strong', fontStyle: 'bold' },
|
||||
@@ -130,7 +130,7 @@ export const vs_dark: IThemeRule[] = [
|
||||
|
||||
/* -------------------------------- Begin hc-black tokens -------------------------------- */
|
||||
export const hc_black: IThemeRule[] = [
|
||||
{ token: '', foreground: 'FFFFFF' },
|
||||
{ token: '', foreground: 'FFFFFF', background: '000000' },
|
||||
{ token: 'invalid', foreground: 'f44747' },
|
||||
{ token: 'emphasis', fontStyle: 'italic' },
|
||||
{ token: 'strong', fontStyle: 'bold' },
|
||||
|
||||
@@ -5,6 +5,124 @@
|
||||
'use strict';
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { ColorId, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import Event, { Emitter } from 'vs/base/common/event';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export class ParsedColor {
|
||||
|
||||
public readonly r: number;
|
||||
public readonly g: number;
|
||||
public readonly b: number;
|
||||
public readonly isLight: boolean;
|
||||
|
||||
constructor(r, g, b) {
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.isLight = ((r + g + b) / (3 * 255) > 0.5);
|
||||
}
|
||||
|
||||
public toCSSHex(): string {
|
||||
return `#${ParsedColor._toTwoDigitHex(this.r)}${ParsedColor._toTwoDigitHex(this.g)}${ParsedColor._toTwoDigitHex(this.b)}`;
|
||||
}
|
||||
|
||||
private static _toTwoDigitHex(n: number): string {
|
||||
let r = n.toString(16);
|
||||
if (r.length !== 2) {
|
||||
return '0' + r;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
export class MinimapTokensColorTracker {
|
||||
private static _INSTANCE: MinimapTokensColorTracker = null;
|
||||
public static getInstance(): MinimapTokensColorTracker {
|
||||
if (!this._INSTANCE) {
|
||||
this._INSTANCE = new MinimapTokensColorTracker();
|
||||
}
|
||||
return this._INSTANCE;
|
||||
}
|
||||
|
||||
private _colors: ParsedColor[];
|
||||
|
||||
private _onDidChange = new Emitter<void>();
|
||||
public onDidChange: Event<void> = this._onDidChange.event;
|
||||
|
||||
private constructor() {
|
||||
this._setColorMap(TokenizationRegistry.getColorMap());
|
||||
TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.changedColorMap) {
|
||||
this._setColorMap(TokenizationRegistry.getColorMap());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _setColorMap(colorMap: Color[]): void {
|
||||
this._colors = [null];
|
||||
for (let colorId = 1; colorId < colorMap.length; colorId++) {
|
||||
const color = colorMap[colorId].toRGBA();
|
||||
this._colors[colorId] = new ParsedColor(color.r, color.g, color.b);
|
||||
}
|
||||
this._onDidChange.fire(void 0);
|
||||
}
|
||||
|
||||
public getColor(colorId: ColorId): ParsedColor {
|
||||
if (colorId < 1 || colorId >= this._colors.length) {
|
||||
// background color (basically invisible)
|
||||
colorId = 2;
|
||||
}
|
||||
return this._colors[colorId];
|
||||
}
|
||||
|
||||
public static _parseColor(color: string): ParsedColor {
|
||||
if (!color) {
|
||||
return new ParsedColor(0, 0, 0);
|
||||
}
|
||||
if (color.charCodeAt(0) === CharCode.Hash) {
|
||||
color = color.substr(1, 6);
|
||||
} else {
|
||||
color = color.substr(0, 6);
|
||||
}
|
||||
if (color.length !== 6) {
|
||||
return new ParsedColor(0, 0, 0);
|
||||
}
|
||||
|
||||
let r = 16 * this._parseHexDigit(color.charCodeAt(0)) + this._parseHexDigit(color.charCodeAt(1));
|
||||
let g = 16 * this._parseHexDigit(color.charCodeAt(2)) + this._parseHexDigit(color.charCodeAt(3));
|
||||
let b = 16 * this._parseHexDigit(color.charCodeAt(4)) + this._parseHexDigit(color.charCodeAt(5));
|
||||
return new ParsedColor(r, g, b);
|
||||
}
|
||||
|
||||
private static _parseHexDigit(charCode: CharCode): number {
|
||||
switch (charCode) {
|
||||
case CharCode.Digit0: return 0;
|
||||
case CharCode.Digit1: return 1;
|
||||
case CharCode.Digit2: return 2;
|
||||
case CharCode.Digit3: return 3;
|
||||
case CharCode.Digit4: return 4;
|
||||
case CharCode.Digit5: return 5;
|
||||
case CharCode.Digit6: return 6;
|
||||
case CharCode.Digit7: return 7;
|
||||
case CharCode.Digit8: return 8;
|
||||
case CharCode.Digit9: return 9;
|
||||
case CharCode.a: return 10;
|
||||
case CharCode.A: return 10;
|
||||
case CharCode.b: return 11;
|
||||
case CharCode.B: return 11;
|
||||
case CharCode.c: return 12;
|
||||
case CharCode.C: return 12;
|
||||
case CharCode.d: return 13;
|
||||
case CharCode.D: return 13;
|
||||
case CharCode.e: return 14;
|
||||
case CharCode.E: return 14;
|
||||
case CharCode.f: return 15;
|
||||
case CharCode.F: return 15;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export const enum Constants {
|
||||
START_CH_CODE = 32, // Space
|
||||
@@ -22,7 +140,6 @@ export const enum Constants {
|
||||
x1_CHAR_WIDTH = 1,
|
||||
|
||||
RGBA_CHANNELS_CNT = 4,
|
||||
CA_CHANNELS_CNT = 2,
|
||||
}
|
||||
|
||||
export class MinimapCharRenderer {
|
||||
@@ -32,119 +149,152 @@ export class MinimapCharRenderer {
|
||||
public readonly x2charData: Uint8ClampedArray;
|
||||
public readonly x1charData: Uint8ClampedArray;
|
||||
|
||||
public readonly x2charDataLight: Uint8ClampedArray;
|
||||
public readonly x1charDataLight: Uint8ClampedArray;
|
||||
|
||||
constructor(x2CharData: Uint8ClampedArray, x1CharData: Uint8ClampedArray) {
|
||||
const x2ExpectedLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CA_CHANNELS_CNT * Constants.CHAR_COUNT;
|
||||
const x2ExpectedLen = Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT;
|
||||
if (x2CharData.length !== x2ExpectedLen) {
|
||||
throw new Error('Invalid x2CharData');
|
||||
}
|
||||
const x1ExpectedLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CA_CHANNELS_CNT * Constants.CHAR_COUNT;
|
||||
const x1ExpectedLen = Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT;
|
||||
if (x1CharData.length !== x1ExpectedLen) {
|
||||
throw new Error('Invalid x1CharData');
|
||||
}
|
||||
this.x2charData = x2CharData;
|
||||
this.x1charData = x1CharData;
|
||||
|
||||
this.x2charDataLight = MinimapCharRenderer.soften(x2CharData, 12 / 15);
|
||||
this.x1charDataLight = MinimapCharRenderer.soften(x1CharData, 50 / 60);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a line height of 4px and a char width of 2px
|
||||
*/
|
||||
public x2RenderChar(target: Uint8ClampedArray, lineLen: number, lineIndex: number, charIndex: number, chCode: number): void {
|
||||
const x2CharData = this.x2charData;
|
||||
|
||||
const outWidth = Constants.x2_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * lineLen;
|
||||
|
||||
if (chCode < Constants.START_CH_CODE || chCode > Constants.END_CH_CODE) {
|
||||
chCode = CharCode.N;
|
||||
private static soften(input: Uint8ClampedArray, ratio: number): Uint8ClampedArray {
|
||||
let result = new Uint8ClampedArray(input.length);
|
||||
for (let i = 0, len = input.length; i < len; i++) {
|
||||
result[i] = input[i] * ratio;
|
||||
}
|
||||
const chIndex = chCode - Constants.START_CH_CODE;
|
||||
|
||||
let sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CA_CHANNELS_CNT;
|
||||
const c1 = x2CharData[sourceOffset];
|
||||
const a1 = x2CharData[sourceOffset + 1];
|
||||
const c2 = x2CharData[sourceOffset + 2];
|
||||
const a2 = x2CharData[sourceOffset + 3];
|
||||
|
||||
const c3 = x2CharData[sourceOffset + 4];
|
||||
const a3 = x2CharData[sourceOffset + 5];
|
||||
const c4 = x2CharData[sourceOffset + 6];
|
||||
const a4 = x2CharData[sourceOffset + 7];
|
||||
|
||||
const c5 = x2CharData[sourceOffset + 8];
|
||||
const a5 = x2CharData[sourceOffset + 9];
|
||||
const c6 = x2CharData[sourceOffset + 10];
|
||||
const a6 = x2CharData[sourceOffset + 11];
|
||||
|
||||
const c7 = x2CharData[sourceOffset + 12];
|
||||
const a7 = x2CharData[sourceOffset + 13];
|
||||
const c8 = x2CharData[sourceOffset + 14];
|
||||
const a8 = x2CharData[sourceOffset + 15];
|
||||
|
||||
let resultOffset = Constants.x2_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * charIndex;
|
||||
target[resultOffset + 0] = c1;
|
||||
target[resultOffset + 1] = c1;
|
||||
target[resultOffset + 2] = c1;
|
||||
target[resultOffset + 3] = a1;
|
||||
target[resultOffset + 4] = c2;
|
||||
target[resultOffset + 5] = c2;
|
||||
target[resultOffset + 6] = c2;
|
||||
target[resultOffset + 7] = a2;
|
||||
resultOffset += outWidth;
|
||||
target[resultOffset + 0] = c3;
|
||||
target[resultOffset + 1] = c3;
|
||||
target[resultOffset + 2] = c3;
|
||||
target[resultOffset + 3] = a3;
|
||||
target[resultOffset + 4] = c4;
|
||||
target[resultOffset + 5] = c4;
|
||||
target[resultOffset + 6] = c4;
|
||||
target[resultOffset + 7] = a4;
|
||||
resultOffset += outWidth;
|
||||
target[resultOffset + 0] = c5;
|
||||
target[resultOffset + 1] = c5;
|
||||
target[resultOffset + 2] = c5;
|
||||
target[resultOffset + 3] = a5;
|
||||
target[resultOffset + 4] = c6;
|
||||
target[resultOffset + 5] = c6;
|
||||
target[resultOffset + 6] = c6;
|
||||
target[resultOffset + 7] = a6;
|
||||
resultOffset += outWidth;
|
||||
target[resultOffset + 0] = c7;
|
||||
target[resultOffset + 1] = c7;
|
||||
target[resultOffset + 2] = c7;
|
||||
target[resultOffset + 3] = a7;
|
||||
target[resultOffset + 4] = c8;
|
||||
target[resultOffset + 5] = c8;
|
||||
target[resultOffset + 6] = c8;
|
||||
target[resultOffset + 7] = a8;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assumes a line height of 2px and a char width of 1px
|
||||
*/
|
||||
public x1RenderChar(target: Uint8ClampedArray, lineLen: number, lineIndex: number, charIndex: number, chCode: number): void {
|
||||
const x1CharData = this.x1charData;
|
||||
|
||||
const outWidth = Constants.x1_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * lineLen;
|
||||
|
||||
if (chCode < Constants.START_CH_CODE || chCode > Constants.END_CH_CODE) {
|
||||
chCode = CharCode.N;
|
||||
private static _getChIndex(chCode: number): number {
|
||||
chCode -= Constants.START_CH_CODE;
|
||||
if (chCode < 0) {
|
||||
chCode += Constants.CHAR_COUNT;
|
||||
}
|
||||
const chIndex = chCode - Constants.START_CH_CODE;
|
||||
return (chCode % Constants.CHAR_COUNT);
|
||||
}
|
||||
|
||||
let sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CA_CHANNELS_CNT;
|
||||
const c1 = x1CharData[sourceOffset];
|
||||
const a1 = x1CharData[sourceOffset + 1];
|
||||
const c2 = x1CharData[sourceOffset + 2];
|
||||
const a2 = x1CharData[sourceOffset + 3];
|
||||
public x2RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: ParsedColor, backgroundColor: ParsedColor): void {
|
||||
if (dx + Constants.x2_CHAR_WIDTH > target.width || dy + Constants.x2_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
const x2CharData = backgroundColor.isLight ? this.x2charDataLight : this.x2charData;
|
||||
const chIndex = MinimapCharRenderer._getChIndex(chCode);
|
||||
|
||||
let resultOffset = Constants.x1_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * charIndex;
|
||||
target[resultOffset + 0] = c1;
|
||||
target[resultOffset + 1] = c1;
|
||||
target[resultOffset + 2] = c1;
|
||||
target[resultOffset + 3] = a1;
|
||||
resultOffset += outWidth;
|
||||
target[resultOffset + 0] = c2;
|
||||
target[resultOffset + 1] = c2;
|
||||
target[resultOffset + 2] = c2;
|
||||
target[resultOffset + 3] = a2;
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const dest = target.data;
|
||||
const sourceOffset = chIndex * Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH;
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
const c = x2CharData[sourceOffset] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 1] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 2] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 3] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 4] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 5] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 6] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
{
|
||||
const c = x2CharData[sourceOffset + 7] / 255;
|
||||
dest[destOffset + 4] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 5] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 6] = backgroundB + deltaB * c;
|
||||
}
|
||||
}
|
||||
|
||||
public x1RenderChar(target: ImageData, dx: number, dy: number, chCode: number, color: ParsedColor, backgroundColor: ParsedColor): void {
|
||||
if (dx + Constants.x1_CHAR_WIDTH > target.width || dy + Constants.x1_CHAR_HEIGHT > target.height) {
|
||||
console.warn('bad render request outside image data');
|
||||
return;
|
||||
}
|
||||
const x1CharData = backgroundColor.isLight ? this.x1charDataLight : this.x1charData;
|
||||
const chIndex = MinimapCharRenderer._getChIndex(chCode);
|
||||
|
||||
const outWidth = target.width * Constants.RGBA_CHANNELS_CNT;
|
||||
|
||||
const backgroundR = backgroundColor.r;
|
||||
const backgroundG = backgroundColor.g;
|
||||
const backgroundB = backgroundColor.b;
|
||||
|
||||
const deltaR = color.r - backgroundR;
|
||||
const deltaG = color.g - backgroundG;
|
||||
const deltaB = color.b - backgroundB;
|
||||
|
||||
const dest = target.data;
|
||||
const sourceOffset = chIndex * Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH;
|
||||
let destOffset = dy * outWidth + dx * Constants.RGBA_CHANNELS_CNT;
|
||||
{
|
||||
const c = x1CharData[sourceOffset] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
|
||||
destOffset += outWidth;
|
||||
{
|
||||
const c = x1CharData[sourceOffset + 1] / 255;
|
||||
dest[destOffset + 0] = backgroundR + deltaR * c;
|
||||
dest[destOffset + 1] = backgroundG + deltaG * c;
|
||||
dest[destOffset + 2] = backgroundB + deltaB * c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { EditorLayoutInfo, OverviewRulerPosition } from 'vs/editor/common/editorCommon';
|
||||
import { RenderMinimap, EditorLayoutInfo, OverviewRulerPosition } from 'vs/editor/common/editorCommon';
|
||||
|
||||
export interface IEditorLayoutProviderOpts {
|
||||
outerWidth: number;
|
||||
@@ -15,15 +15,20 @@ export interface IEditorLayoutProviderOpts {
|
||||
|
||||
showLineNumbers: boolean;
|
||||
lineNumbersMinChars: number;
|
||||
lineDecorationsWidth: number;
|
||||
maxDigitWidth: number;
|
||||
|
||||
maxLineNumber: number;
|
||||
|
||||
lineDecorationsWidth: number;
|
||||
|
||||
typicalHalfwidthCharacterWidth: number;
|
||||
maxDigitWidth: number;
|
||||
|
||||
verticalScrollbarWidth: number;
|
||||
verticalScrollbarHasArrows: boolean;
|
||||
scrollbarArrowSize: number;
|
||||
horizontalScrollbarHeight: number;
|
||||
|
||||
minimap: boolean;
|
||||
pixelRatio: number;
|
||||
}
|
||||
|
||||
export class EditorLayoutProvider {
|
||||
@@ -34,13 +39,16 @@ export class EditorLayoutProvider {
|
||||
const lineHeight = _opts.lineHeight | 0;
|
||||
const showLineNumbers = Boolean(_opts.showLineNumbers);
|
||||
const lineNumbersMinChars = _opts.lineNumbersMinChars | 0;
|
||||
const lineDecorationsWidth = _opts.lineDecorationsWidth | 0;
|
||||
const maxDigitWidth = Number(_opts.maxDigitWidth);
|
||||
const maxLineNumber = _opts.maxLineNumber | 0;
|
||||
const lineDecorationsWidth = _opts.lineDecorationsWidth | 0;
|
||||
const typicalHalfwidthCharacterWidth = Number(_opts.typicalHalfwidthCharacterWidth);
|
||||
const maxDigitWidth = Number(_opts.maxDigitWidth);
|
||||
const verticalScrollbarWidth = _opts.verticalScrollbarWidth | 0;
|
||||
const verticalScrollbarHasArrows = Boolean(_opts.verticalScrollbarHasArrows);
|
||||
const scrollbarArrowSize = _opts.scrollbarArrowSize | 0;
|
||||
const horizontalScrollbarHeight = _opts.horizontalScrollbarHeight | 0;
|
||||
const minimap = Boolean(_opts.minimap);
|
||||
const pixelRatio = Number(_opts.pixelRatio);
|
||||
|
||||
let lineNumbersWidth = 0;
|
||||
if (showLineNumbers) {
|
||||
@@ -53,13 +61,48 @@ export class EditorLayoutProvider {
|
||||
glyphMarginWidth = lineHeight;
|
||||
}
|
||||
|
||||
let contentWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
|
||||
|
||||
let glyphMarginLeft = 0;
|
||||
let lineNumbersLeft = glyphMarginLeft + glyphMarginWidth;
|
||||
let decorationsLeft = lineNumbersLeft + lineNumbersWidth;
|
||||
let contentLeft = decorationsLeft + lineDecorationsWidth;
|
||||
|
||||
let remainingWidth = outerWidth - glyphMarginWidth - lineNumbersWidth - lineDecorationsWidth;
|
||||
|
||||
let renderMinimap: RenderMinimap;
|
||||
let minimapWidth: number;
|
||||
let contentWidth: number;
|
||||
if (!minimap) {
|
||||
minimapWidth = 0;
|
||||
renderMinimap = RenderMinimap.None;
|
||||
contentWidth = remainingWidth;
|
||||
} else {
|
||||
let minimapCharWidth: number;
|
||||
if (pixelRatio >= 2) {
|
||||
renderMinimap = RenderMinimap.Large;
|
||||
minimapCharWidth = 2 / pixelRatio;
|
||||
} else {
|
||||
renderMinimap = RenderMinimap.Small;
|
||||
minimapCharWidth = 1 / pixelRatio;
|
||||
}
|
||||
|
||||
// Given:
|
||||
// viewportColumn = (contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth
|
||||
// minimapWidth = viewportColumn * minimapCharWidth
|
||||
// contentWidth = remainingWidth - minimapWidth
|
||||
// What are good values for contentWidth and minimapWidth ?
|
||||
|
||||
// minimapWidth = ((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth) * minimapCharWidth
|
||||
// typicalHalfwidthCharacterWidth * minimapWidth = (contentWidth - verticalScrollbarWidth) * minimapCharWidth
|
||||
// typicalHalfwidthCharacterWidth * minimapWidth = (remainingWidth - minimapWidth - verticalScrollbarWidth) * minimapCharWidth
|
||||
// (typicalHalfwidthCharacterWidth + minimapCharWidth) * minimapWidth = (remainingWidth - verticalScrollbarWidth) * minimapCharWidth
|
||||
// minimapWidth = ((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth)
|
||||
|
||||
minimapWidth = Math.max(0, Math.floor(((remainingWidth - verticalScrollbarWidth) * minimapCharWidth) / (typicalHalfwidthCharacterWidth + minimapCharWidth)));
|
||||
contentWidth = remainingWidth - minimapWidth;
|
||||
}
|
||||
|
||||
let viewportColumn = Math.max(1, Math.floor((contentWidth - verticalScrollbarWidth) / typicalHalfwidthCharacterWidth));
|
||||
|
||||
let verticalArrowSize = (verticalScrollbarHasArrows ? scrollbarArrowSize : 0);
|
||||
|
||||
return new EditorLayoutInfo({
|
||||
@@ -82,6 +125,11 @@ export class EditorLayoutProvider {
|
||||
contentWidth: contentWidth,
|
||||
contentHeight: outerHeight,
|
||||
|
||||
renderMinimap: renderMinimap,
|
||||
minimapWidth: minimapWidth,
|
||||
|
||||
viewportColumn: viewportColumn,
|
||||
|
||||
verticalScrollbarWidth: verticalScrollbarWidth,
|
||||
horizontalScrollbarHeight: horizontalScrollbarHeight,
|
||||
|
||||
|
||||
@@ -15,6 +15,21 @@ export const enum RenderWhitespace {
|
||||
All = 2
|
||||
}
|
||||
|
||||
class LinePart {
|
||||
_linePartBrand: void;
|
||||
|
||||
/**
|
||||
* last char index of this token (not inclusive).
|
||||
*/
|
||||
public readonly endIndex: number;
|
||||
public readonly type: string;
|
||||
|
||||
constructor(endIndex: number, type: string) {
|
||||
this.endIndex = endIndex;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
export class RenderLineInput {
|
||||
|
||||
public readonly useMonospaceOptimizations: boolean;
|
||||
@@ -236,7 +251,7 @@ class ResolvedRenderLineInput {
|
||||
public readonly lineContent: string,
|
||||
public readonly len: number,
|
||||
public readonly isOverflowing: boolean,
|
||||
public readonly tokens: ViewLineToken[],
|
||||
public readonly parts: LinePart[],
|
||||
public readonly containsForeignElements: boolean,
|
||||
public readonly tabSize: number,
|
||||
public readonly containsRTL: boolean,
|
||||
@@ -305,25 +320,17 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput
|
||||
* In the rendering phase, characters are always looped until token.endIndex.
|
||||
* Ensure that all tokens end before `len` and the last one ends precisely at `len`.
|
||||
*/
|
||||
function removeOverflowing(tokens: ViewLineToken[], len: number): ViewLineToken[] {
|
||||
if (tokens.length === 0) {
|
||||
return tokens;
|
||||
}
|
||||
if (tokens[tokens.length - 1].endIndex === len) {
|
||||
return tokens;
|
||||
}
|
||||
let result: ViewLineToken[] = [];
|
||||
function removeOverflowing(tokens: ViewLineToken[], len: number): LinePart[] {
|
||||
let result: LinePart[] = [];
|
||||
for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
|
||||
const endIndex = tokens[tokenIndex].endIndex;
|
||||
if (endIndex === len) {
|
||||
result[tokenIndex] = tokens[tokenIndex];
|
||||
const token = tokens[tokenIndex];
|
||||
const endIndex = token.endIndex;
|
||||
const type = token.getType();
|
||||
if (endIndex >= len) {
|
||||
result[tokenIndex] = new LinePart(len, type);
|
||||
break;
|
||||
}
|
||||
if (endIndex > len) {
|
||||
result[tokenIndex] = new ViewLineToken(len, tokens[tokenIndex].type);
|
||||
break;
|
||||
}
|
||||
result[tokenIndex] = tokens[tokenIndex];
|
||||
result[tokenIndex] = new LinePart(endIndex, type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -340,9 +347,9 @@ const enum Constants {
|
||||
* It appears that having very large spans causes very slow reading of character positions.
|
||||
* So here we try to avoid that.
|
||||
*/
|
||||
function splitLargeTokens(tokens: ViewLineToken[]): ViewLineToken[] {
|
||||
function splitLargeTokens(tokens: LinePart[]): LinePart[] {
|
||||
let lastTokenEndIndex = 0;
|
||||
let result: ViewLineToken[] = [], resultLen = 0;
|
||||
let result: LinePart[] = [], resultLen = 0;
|
||||
for (let i = 0, len = tokens.length; i < len; i++) {
|
||||
const token = tokens[i];
|
||||
const tokenEndIndex = token.endIndex;
|
||||
@@ -352,9 +359,9 @@ function splitLargeTokens(tokens: ViewLineToken[]): ViewLineToken[] {
|
||||
const piecesCount = Math.ceil(diff / Constants.LongToken);
|
||||
for (let j = 1; j < piecesCount; j++) {
|
||||
let pieceEndIndex = lastTokenEndIndex + (j * Constants.LongToken);
|
||||
result[resultLen++] = new ViewLineToken(pieceEndIndex, tokenType);
|
||||
result[resultLen++] = new LinePart(pieceEndIndex, tokenType);
|
||||
}
|
||||
result[resultLen++] = new ViewLineToken(tokenEndIndex, tokenType);
|
||||
result[resultLen++] = new LinePart(tokenEndIndex, tokenType);
|
||||
} else {
|
||||
result[resultLen++] = token;
|
||||
}
|
||||
@@ -369,15 +376,15 @@ function splitLargeTokens(tokens: ViewLineToken[]): ViewLineToken[] {
|
||||
* Moreover, a token is created for every visual indent because on some fonts the glyphs used for rendering whitespace (→ or ·) do not have the same width as .
|
||||
* The rendering phase will generate `style="width:..."` for these tokens.
|
||||
*/
|
||||
function _applyRenderWhitespace(lineContent: string, len: number, tokens: ViewLineToken[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, onlyBoundary: boolean): ViewLineToken[] {
|
||||
function _applyRenderWhitespace(lineContent: string, len: number, tokens: LinePart[], fauxIndentLength: number, tabSize: number, useMonospaceOptimizations: boolean, onlyBoundary: boolean): LinePart[] {
|
||||
|
||||
let result: ViewLineToken[] = [], resultLen = 0;
|
||||
let result: LinePart[] = [], resultLen = 0;
|
||||
let tokenIndex = 0;
|
||||
let tokenType = tokens[tokenIndex].type;
|
||||
let tokenEndIndex = tokens[tokenIndex].endIndex;
|
||||
|
||||
if (fauxIndentLength > 0) {
|
||||
result[resultLen++] = new ViewLineToken(fauxIndentLength, '');
|
||||
result[resultLen++] = new LinePart(fauxIndentLength, '');
|
||||
}
|
||||
|
||||
let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(lineContent);
|
||||
@@ -433,13 +440,13 @@ function _applyRenderWhitespace(lineContent: string, len: number, tokens: ViewLi
|
||||
// was in whitespace token
|
||||
if (!isInWhitespace || (!useMonospaceOptimizations && tmpIndent >= tabSize)) {
|
||||
// leaving whitespace token or entering a new indent
|
||||
result[resultLen++] = new ViewLineToken(charIndex, 'vs-whitespace');
|
||||
result[resultLen++] = new LinePart(charIndex, 'vs-whitespace');
|
||||
tmpIndent = tmpIndent % tabSize;
|
||||
}
|
||||
} else {
|
||||
// was in regular token
|
||||
if (charIndex === tokenEndIndex || (isInWhitespace && charIndex > fauxIndentLength)) {
|
||||
result[resultLen++] = new ViewLineToken(charIndex, tokenType);
|
||||
result[resultLen++] = new LinePart(charIndex, tokenType);
|
||||
tmpIndent = tmpIndent % tabSize;
|
||||
}
|
||||
}
|
||||
@@ -461,10 +468,10 @@ function _applyRenderWhitespace(lineContent: string, len: number, tokens: ViewLi
|
||||
|
||||
if (wasInWhitespace) {
|
||||
// was in whitespace token
|
||||
result[resultLen++] = new ViewLineToken(len, 'vs-whitespace');
|
||||
result[resultLen++] = new LinePart(len, 'vs-whitespace');
|
||||
} else {
|
||||
// was in regular token
|
||||
result[resultLen++] = new ViewLineToken(len, tokenType);
|
||||
result[resultLen++] = new LinePart(len, tokenType);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -474,13 +481,13 @@ function _applyRenderWhitespace(lineContent: string, len: number, tokens: ViewLi
|
||||
* Inline decorations are "merged" on top of tokens.
|
||||
* Special care must be taken when multiple inline decorations are at play and they overlap.
|
||||
*/
|
||||
function _applyInlineDecorations(lineContent: string, len: number, tokens: ViewLineToken[], _lineDecorations: Decoration[]): ViewLineToken[] {
|
||||
function _applyInlineDecorations(lineContent: string, len: number, tokens: LinePart[], _lineDecorations: Decoration[]): LinePart[] {
|
||||
_lineDecorations.sort(Decoration.compare);
|
||||
const lineDecorations = LineDecorationsNormalizer.normalize(_lineDecorations);
|
||||
const lineDecorationsLen = lineDecorations.length;
|
||||
|
||||
let lineDecorationIndex = 0;
|
||||
let result: ViewLineToken[] = [], resultLen = 0, lastResultEndIndex = 0;
|
||||
let result: LinePart[] = [], resultLen = 0, lastResultEndIndex = 0;
|
||||
for (let tokenIndex = 0, len = tokens.length; tokenIndex < len; tokenIndex++) {
|
||||
const token = tokens[tokenIndex];
|
||||
const tokenEndIndex = token.endIndex;
|
||||
@@ -491,25 +498,25 @@ function _applyInlineDecorations(lineContent: string, len: number, tokens: ViewL
|
||||
|
||||
if (lineDecoration.startOffset > lastResultEndIndex) {
|
||||
lastResultEndIndex = lineDecoration.startOffset;
|
||||
result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType);
|
||||
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
|
||||
}
|
||||
|
||||
if (lineDecoration.endOffset + 1 <= tokenEndIndex) {
|
||||
// This line decoration ends before this token ends
|
||||
lastResultEndIndex = lineDecoration.endOffset + 1;
|
||||
result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
|
||||
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
|
||||
lineDecorationIndex++;
|
||||
} else {
|
||||
// This line decoration continues on to the next token
|
||||
lastResultEndIndex = tokenEndIndex;
|
||||
result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
|
||||
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType + ' ' + lineDecoration.className);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokenEndIndex > lastResultEndIndex) {
|
||||
lastResultEndIndex = tokenEndIndex;
|
||||
result[resultLen++] = new ViewLineToken(lastResultEndIndex, tokenType);
|
||||
result[resultLen++] = new LinePart(lastResultEndIndex, tokenType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -526,33 +533,33 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
|
||||
const lineContent = input.lineContent;
|
||||
const len = input.len;
|
||||
const isOverflowing = input.isOverflowing;
|
||||
const tokens = input.tokens;
|
||||
const parts = input.parts;
|
||||
const tabSize = input.tabSize;
|
||||
const containsRTL = input.containsRTL;
|
||||
const spaceWidth = input.spaceWidth;
|
||||
const renderWhitespace = input.renderWhitespace;
|
||||
const renderControlCharacters = input.renderControlCharacters;
|
||||
|
||||
const characterMapping = new CharacterMapping(len + 1, tokens.length);
|
||||
const characterMapping = new CharacterMapping(len + 1, parts.length);
|
||||
|
||||
let charIndex = 0;
|
||||
let tabsCharDelta = 0;
|
||||
let charOffsetInPart = 0;
|
||||
|
||||
let out = '<span>';
|
||||
for (let tokenIndex = 0, tokensLen = tokens.length; tokenIndex < tokensLen; tokenIndex++) {
|
||||
const token = tokens[tokenIndex];
|
||||
const tokenEndIndex = token.endIndex;
|
||||
const tokenType = token.type;
|
||||
const tokenRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (tokenType.indexOf('vs-whitespace') >= 0));
|
||||
for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
|
||||
const part = parts[partIndex];
|
||||
const partEndIndex = part.endIndex;
|
||||
const partType = part.type;
|
||||
const partRendersWhitespace = (renderWhitespace !== RenderWhitespace.None && (partType.indexOf('vs-whitespace') >= 0));
|
||||
charOffsetInPart = 0;
|
||||
|
||||
if (tokenRendersWhitespace) {
|
||||
if (partRendersWhitespace) {
|
||||
|
||||
let partContentCnt = 0;
|
||||
let partContent = '';
|
||||
for (; charIndex < tokenEndIndex; charIndex++) {
|
||||
characterMapping.setPartData(charIndex, tokenIndex, charOffsetInPart);
|
||||
for (; charIndex < partEndIndex; charIndex++) {
|
||||
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
|
||||
const charCode = lineContent.charCodeAt(charIndex);
|
||||
|
||||
if (charCode === CharCode.Tab) {
|
||||
@@ -578,11 +585,11 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
|
||||
charOffsetInPart++;
|
||||
}
|
||||
|
||||
characterMapping.setPartLength(tokenIndex, partContentCnt);
|
||||
characterMapping.setPartLength(partIndex, partContentCnt);
|
||||
if (fontIsMonospace) {
|
||||
out += `<span class="${tokenType}">${partContent}</span>`;
|
||||
out += `<span class="${partType}">${partContent}</span>`;
|
||||
} else {
|
||||
out += `<span class="${tokenType}" style="width:${spaceWidth * partContentCnt}px">${partContent}</span>`;
|
||||
out += `<span class="${partType}" style="width:${spaceWidth * partContentCnt}px">${partContent}</span>`;
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -590,8 +597,8 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
|
||||
let partContentCnt = 0;
|
||||
let partContent = '';
|
||||
|
||||
for (; charIndex < tokenEndIndex; charIndex++) {
|
||||
characterMapping.setPartData(charIndex, tokenIndex, charOffsetInPart);
|
||||
for (; charIndex < partEndIndex; charIndex++) {
|
||||
characterMapping.setPartData(charIndex, partIndex, charOffsetInPart);
|
||||
const charCode = lineContent.charCodeAt(charIndex);
|
||||
|
||||
switch (charCode) {
|
||||
@@ -656,11 +663,11 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
|
||||
charOffsetInPart++;
|
||||
}
|
||||
|
||||
characterMapping.setPartLength(tokenIndex, partContentCnt);
|
||||
characterMapping.setPartLength(partIndex, partContentCnt);
|
||||
if (containsRTL) {
|
||||
out += `<span dir="ltr" class="${tokenType}">${partContent}</span>`;
|
||||
out += `<span dir="ltr" class="${partType}">${partContent}</span>`;
|
||||
} else {
|
||||
out += `<span class="${tokenType}">${partContent}</span>`;
|
||||
out += `<span class="${partType}">${partContent}</span>`;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -668,7 +675,7 @@ function _renderLine(input: ResolvedRenderLineInput): RenderLineOutput {
|
||||
|
||||
// When getting client rects for the last character, we will position the
|
||||
// text range at the end of the span, insteaf of at the beginning of next span
|
||||
characterMapping.setPartData(len, tokens.length - 1, charOffsetInPart);
|
||||
characterMapping.setPartData(len, parts.length - 1, charOffsetInPart);
|
||||
|
||||
if (isOverflowing) {
|
||||
out += `<span class="vs-whitespace">…</span>`;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
import { ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
|
||||
export class OutputPosition {
|
||||
_outputPositionBrand: void;
|
||||
@@ -48,7 +48,8 @@ export interface ISplitLine {
|
||||
getViewLineContent(model: IModel, modelLineNumber: number, outputLineIndex: number): string;
|
||||
getViewLineMinColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number;
|
||||
getViewLineMaxColumn(model: IModel, modelLineNumber: number, outputLineIndex: number): number;
|
||||
getViewLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number): OutputLineRenderingData;
|
||||
getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData;
|
||||
getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void;
|
||||
|
||||
getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number;
|
||||
getViewPositionOfModelPosition(deltaLineNumber: number, inputColumn: number): Position;
|
||||
@@ -87,10 +88,10 @@ class VisibleIdentitySplitLine implements ISplitLine {
|
||||
return model.getLineMaxColumn(modelLineNumber);
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number): OutputLineRenderingData {
|
||||
public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData {
|
||||
let lineTokens = model.getLineTokens(modelLineNumber, true);
|
||||
let lineContent = lineTokens.getLineContent();
|
||||
return new OutputLineRenderingData(
|
||||
return new ViewLineData(
|
||||
lineContent,
|
||||
1,
|
||||
lineContent.length + 1,
|
||||
@@ -98,6 +99,14 @@ class VisibleIdentitySplitLine implements ISplitLine {
|
||||
);
|
||||
}
|
||||
|
||||
public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void {
|
||||
if (!needed[globalStartIndex]) {
|
||||
result[globalStartIndex] = null;
|
||||
return;
|
||||
}
|
||||
result[globalStartIndex] = this.getViewLineData(model, modelLineNumber, 0);
|
||||
}
|
||||
|
||||
public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number {
|
||||
return outputColumn;
|
||||
}
|
||||
@@ -140,7 +149,11 @@ class InvisibleIdentitySplitLine implements ISplitLine {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number): OutputLineRenderingData {
|
||||
public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
|
||||
public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
|
||||
@@ -229,7 +242,7 @@ export class SplitLine implements ISplitLine {
|
||||
return this.getViewLineContent(model, modelLineNumber, outputLineIndex).length + 1;
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(model: IModel, modelLineNumber: number, outputLineIndex: number): OutputLineRenderingData {
|
||||
public getViewLineData(model: IModel, modelLineNumber: number, outputLineIndex: number): ViewLineData {
|
||||
if (!this._isVisible) {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
@@ -251,7 +264,7 @@ export class SplitLine implements ISplitLine {
|
||||
}
|
||||
let lineTokens = model.getLineTokens(modelLineNumber, true);
|
||||
|
||||
return new OutputLineRenderingData(
|
||||
return new ViewLineData(
|
||||
lineContent,
|
||||
minColumn,
|
||||
maxColumn,
|
||||
@@ -259,6 +272,21 @@ export class SplitLine implements ISplitLine {
|
||||
);
|
||||
}
|
||||
|
||||
public getViewLinesData(model: IModel, modelLineNumber: number, fromOuputLineIndex: number, toOutputLineIndex: number, globalStartIndex: number, needed: boolean[], result: ViewLineData[]): void {
|
||||
if (!this._isVisible) {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
|
||||
for (let outputLineIndex = fromOuputLineIndex; outputLineIndex < toOutputLineIndex; outputLineIndex++) {
|
||||
let globalIndex = globalStartIndex + outputLineIndex - fromOuputLineIndex;
|
||||
if (!needed[globalIndex]) {
|
||||
result[globalIndex] = null;
|
||||
continue;
|
||||
}
|
||||
result[globalIndex] = this.getViewLineData(model, modelLineNumber, outputLineIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public getModelColumnOfViewPosition(outputLineIndex: number, outputColumn: number): number {
|
||||
if (!this._isVisible) {
|
||||
throw new Error('Not supported');
|
||||
@@ -723,14 +751,53 @@ export class SplitLinesCollection {
|
||||
return this.lines[lineIndex].getViewLineMaxColumn(this.model, lineIndex + 1, remainder);
|
||||
}
|
||||
|
||||
public getViewLineRenderingData(viewLineNumber: number): OutputLineRenderingData {
|
||||
public getViewLineData(viewLineNumber: number): ViewLineData {
|
||||
this._ensureValidState();
|
||||
viewLineNumber = this._toValidViewLineNumber(viewLineNumber);
|
||||
let r = this.prefixSumComputer.getIndexOf(viewLineNumber - 1);
|
||||
let lineIndex = r.index;
|
||||
let remainder = r.remainder;
|
||||
|
||||
return this.lines[lineIndex].getViewLineRenderingData(this.model, lineIndex + 1, remainder);
|
||||
return this.lines[lineIndex].getViewLineData(this.model, lineIndex + 1, remainder);
|
||||
}
|
||||
|
||||
public getViewLinesData(viewStartLineNumber: number, viewEndLineNumber: number, needed: boolean[]): ViewLineData[] {
|
||||
this._ensureValidState();
|
||||
|
||||
viewStartLineNumber = this._toValidViewLineNumber(viewStartLineNumber);
|
||||
viewEndLineNumber = this._toValidViewLineNumber(viewEndLineNumber);
|
||||
|
||||
let start = this.prefixSumComputer.getIndexOf(viewStartLineNumber - 1);
|
||||
let viewLineNumber = viewStartLineNumber;
|
||||
let startModelLineIndex = start.index;
|
||||
let startRemainder = start.remainder;
|
||||
|
||||
let result: ViewLineData[] = [];
|
||||
for (let modelLineIndex = startModelLineIndex, len = this.model.getLineCount(); modelLineIndex < len; modelLineIndex++) {
|
||||
let line = this.lines[modelLineIndex];
|
||||
if (!line.isVisible()) {
|
||||
continue;
|
||||
}
|
||||
let fromViewLineIndex = (modelLineIndex === startModelLineIndex ? startRemainder : 0);
|
||||
let remainingViewLineCount = line.getViewLineCount() - fromViewLineIndex;
|
||||
|
||||
let lastLine = false;
|
||||
if (viewLineNumber + remainingViewLineCount > viewEndLineNumber) {
|
||||
lastLine = true;
|
||||
remainingViewLineCount = viewEndLineNumber - viewLineNumber + 1;
|
||||
}
|
||||
let toViewLineIndex = fromViewLineIndex + remainingViewLineCount;
|
||||
|
||||
line.getViewLinesData(this.model, modelLineIndex + 1, fromViewLineIndex, toViewLineIndex, viewLineNumber - viewStartLineNumber, needed, result);
|
||||
|
||||
viewLineNumber += remainingViewLineCount;
|
||||
|
||||
if (lastLine) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public validateViewPosition(viewLineNumber: number, viewColumn: number, expectedModelPosition: Position): Position {
|
||||
@@ -805,36 +872,3 @@ export class SplitLinesCollection {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
export class OutputLineRenderingData {
|
||||
_outputLineRenderingDataBrand: void;
|
||||
|
||||
/**
|
||||
* The content at this view line.
|
||||
*/
|
||||
public readonly content: string;
|
||||
/**
|
||||
* The minimum allowed column at this view line.
|
||||
*/
|
||||
public readonly minColumn: number;
|
||||
/**
|
||||
* The maximum allowed column at this view line.
|
||||
*/
|
||||
public readonly maxColumn: number;
|
||||
/**
|
||||
* The tokens at this view line.
|
||||
*/
|
||||
public readonly tokens: ViewLineToken[];
|
||||
|
||||
constructor(
|
||||
content: string,
|
||||
minColumn: number,
|
||||
maxColumn: number,
|
||||
tokens: ViewLineToken[]
|
||||
) {
|
||||
this.content = content;
|
||||
this.minColumn = minColumn;
|
||||
this.maxColumn = maxColumn;
|
||||
this.tokens = tokens;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ export interface IViewModel extends IEventEmitter {
|
||||
|
||||
getDecorationsInViewport(visibleRange: Range): ViewModelDecoration[];
|
||||
getViewLineRenderingData(visibleRange: Range, lineNumber: number): ViewLineRenderingData;
|
||||
getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData;
|
||||
|
||||
getTabSize(): number;
|
||||
getLineCount(): number;
|
||||
@@ -87,6 +88,55 @@ export interface IViewModel extends IEventEmitter {
|
||||
getModelLineContent(modelLineNumber: number): string;
|
||||
getModelLineMaxColumn(modelLineNumber: number): number;
|
||||
validateModelPosition(modelPosition: IPosition): Position;
|
||||
|
||||
getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
|
||||
getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string;
|
||||
}
|
||||
|
||||
export class MinimapLinesRenderingData {
|
||||
public readonly tabSize: number;
|
||||
public readonly data: ViewLineData[];
|
||||
|
||||
constructor(
|
||||
tabSize: number,
|
||||
data: ViewLineData[]
|
||||
) {
|
||||
this.tabSize = tabSize;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewLineData {
|
||||
_viewLineDataBrand: void;
|
||||
|
||||
/**
|
||||
* The content at this view line.
|
||||
*/
|
||||
public readonly content: string;
|
||||
/**
|
||||
* The minimum allowed column at this view line.
|
||||
*/
|
||||
public readonly minColumn: number;
|
||||
/**
|
||||
* The maximum allowed column at this view line.
|
||||
*/
|
||||
public readonly maxColumn: number;
|
||||
/**
|
||||
* The tokens at this view line.
|
||||
*/
|
||||
public readonly tokens: ViewLineToken[];
|
||||
|
||||
constructor(
|
||||
content: string,
|
||||
minColumn: number,
|
||||
maxColumn: number,
|
||||
tokens: ViewLineToken[]
|
||||
) {
|
||||
this.content = content;
|
||||
this.minColumn = minColumn;
|
||||
this.maxColumn = maxColumn;
|
||||
this.tokens = tokens;
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewLineRenderingData {
|
||||
|
||||
@@ -11,9 +11,11 @@ import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { tokenizeLineToHTML } from 'vs/editor/common/modes/textToHtmlTokenizer';
|
||||
import { ViewModelCursors } from 'vs/editor/common/viewModel/viewModelCursors';
|
||||
import { ViewModelDecorations } from 'vs/editor/common/viewModel/viewModelDecorations';
|
||||
import { ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { MinimapLinesRenderingData, ViewLineRenderingData, ViewModelDecoration, IViewModel, ICoordinatesConverter } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { SplitLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection';
|
||||
|
||||
export class CoordinatesConverter implements ICoordinatesConverter {
|
||||
@@ -518,7 +520,7 @@ export class ViewModel extends EventEmitter implements IViewModel {
|
||||
let mightContainRTL = this.model.mightContainRTL();
|
||||
let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
|
||||
let tabSize = this.getTabSize();
|
||||
let lineData = this.lines.getViewLineRenderingData(lineNumber);
|
||||
let lineData = this.lines.getViewLineData(lineNumber);
|
||||
let allInlineDecorations = this.decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
|
||||
let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber];
|
||||
|
||||
@@ -534,6 +536,14 @@ export class ViewModel extends EventEmitter implements IViewModel {
|
||||
);
|
||||
}
|
||||
|
||||
public getMinimapLinesRenderingData(startLineNumber: number, endLineNumber: number, needed: boolean[]): MinimapLinesRenderingData {
|
||||
let result = this.lines.getViewLinesData(startLineNumber, endLineNumber, needed);
|
||||
return new MinimapLinesRenderingData(
|
||||
this.getTabSize(),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
public getAllOverviewRulerDecorations(): ViewModelDecoration[] {
|
||||
return this.decorations.getAllOverviewRulerDecorations();
|
||||
}
|
||||
@@ -558,4 +568,96 @@ export class ViewModel extends EventEmitter implements IViewModel {
|
||||
public validateModelPosition(position: editorCommon.IPosition): Position {
|
||||
return this.model.validatePosition(position);
|
||||
}
|
||||
|
||||
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
let newLineCharacter = this.getEOL();
|
||||
|
||||
if (ranges.length === 1) {
|
||||
let range: Range = ranges[0];
|
||||
if (range.isEmpty()) {
|
||||
if (enableEmptySelectionClipboard) {
|
||||
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
|
||||
return this.getModelLineContent(modelLineNumber) + newLineCharacter;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return this.getValueInRange(range, editorCommon.EndOfLinePreference.TextDefined);
|
||||
} else {
|
||||
ranges = ranges.slice(0).sort(Range.compareRangesUsingStarts);
|
||||
let result: string[] = [];
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
result.push(this.getValueInRange(ranges[i], editorCommon.EndOfLinePreference.TextDefined));
|
||||
}
|
||||
|
||||
return result.join(newLineCharacter);
|
||||
}
|
||||
}
|
||||
|
||||
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
// TODO: adopt new view line tokens.
|
||||
let rules: { [key: string]: string } = {};
|
||||
let colorMap = TokenizationRegistry.getColorMap();
|
||||
for (let i = 1, len = colorMap.length; i < len; i++) {
|
||||
let color = colorMap[i];
|
||||
rules[`mtk${i}`] = `color: ${color.toRGBHex()};`;
|
||||
}
|
||||
rules['mtki'] = 'font-style: italic;';
|
||||
rules['mtkb'] = 'font-weight: bold;';
|
||||
rules['mtku'] = 'text-decoration: underline;';
|
||||
|
||||
let defaultForegroundColor = colorMap[1].toRGBHex();
|
||||
let defaultBackgroundColor = colorMap[2].toRGBHex();
|
||||
|
||||
let fontInfo = this.configuration.editor.fontInfo;
|
||||
|
||||
let output = `<div style="color: ${defaultForegroundColor}; background-color: ${defaultBackgroundColor};` +
|
||||
`font-family: ${fontInfo.fontFamily}; font-weight: ${fontInfo.fontWeight}; font-size: ${fontInfo.fontSize}px; line-height: ${fontInfo.lineHeight}px">`;
|
||||
|
||||
if (ranges.length === 1) {
|
||||
let range: Range = ranges[0];
|
||||
|
||||
if (range.isEmpty()) {
|
||||
if (enableEmptySelectionClipboard) {
|
||||
let modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber;
|
||||
let viewLineStart = new Position(range.startLineNumber, 1);
|
||||
let viewLineEnd = new Position(range.startLineNumber, this.getLineMaxColumn(range.startLineNumber));
|
||||
let startOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).column - 1;
|
||||
let endOffset = this.coordinatesConverter.convertViewPositionToModelPosition(viewLineEnd).column - 1;
|
||||
let viewLineRenderingData = this.getViewLineRenderingData(new Range(viewLineStart.lineNumber, viewLineStart.column, viewLineEnd.lineNumber, viewLineEnd.column), modelLineNumber);
|
||||
let html = tokenizeLineToHTML(this.getModelLineContent(modelLineNumber),
|
||||
viewLineRenderingData.tokens,
|
||||
rules,
|
||||
{
|
||||
startOffset: startOffset,
|
||||
endOffset: endOffset,
|
||||
tabSize: this.getTabSize()
|
||||
});
|
||||
output += `${html}`;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
} else {
|
||||
for (let i = 0, lineCount = range.endLineNumber - range.startLineNumber; i <= lineCount; i++) {
|
||||
let viewLineRenderingData = this.getViewLineRenderingData(range, range.startLineNumber + i);
|
||||
let lineContent = viewLineRenderingData.content;
|
||||
let startOffset = i === 0 ? range.startColumn - 1 : 0;
|
||||
let endOffset = i === lineCount ? range.endColumn - 1 : lineContent.length;
|
||||
|
||||
let html = tokenizeLineToHTML(lineContent, viewLineRenderingData.tokens, rules,
|
||||
{
|
||||
startOffset: startOffset,
|
||||
endOffset: endOffset,
|
||||
tabSize: this.getTabSize()
|
||||
});
|
||||
output += `${html}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output += '</div>';
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation
|
||||
import { findFocusedEditor } from 'vs/editor/common/config/config';
|
||||
import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { editorAction, IActionOptions, EditorAction } from 'vs/editor/common/editorCommonExtensions';
|
||||
import { CopyOptions } from 'vs/editor/common/controller/textAreaHandler';
|
||||
|
||||
import EditorContextKeys = editorCommon.EditorContextKeys;
|
||||
|
||||
@@ -25,6 +26,14 @@ function conditionalEditorAction(testCommand: string) {
|
||||
return editorAction;
|
||||
}
|
||||
|
||||
function conditionalCopyWithSyntaxHighlighting() {
|
||||
if (browser.isEdgeOrIE || !browser.supportsExecCommand('copy')) {
|
||||
return () => { };
|
||||
}
|
||||
|
||||
return editorAction;
|
||||
}
|
||||
|
||||
abstract class ExecCommandAction extends EditorAction {
|
||||
|
||||
private browserCommand: string;
|
||||
@@ -136,3 +145,32 @@ class ExecCommandPasteAction extends ExecCommandAction {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@conditionalCopyWithSyntaxHighlighting()
|
||||
class ExecCommandCopyWithSyntaxHighlightingAction extends ExecCommandAction {
|
||||
|
||||
constructor() {
|
||||
super('copy', {
|
||||
id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction',
|
||||
label: nls.localize('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"),
|
||||
alias: 'Copy With Syntax Highlighting',
|
||||
precondition: null,
|
||||
kbOpts: {
|
||||
kbExpr: EditorContextKeys.TextFocus,
|
||||
primary: null
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public run(accessor: ServicesAccessor, editor: editorCommon.ICommonCodeEditor): void {
|
||||
var enableEmptySelectionClipboard = editor.getConfiguration().contribInfo.emptySelectionClipboard && browser.enableEmptySelectionClipboard;
|
||||
|
||||
if (!enableEmptySelectionClipboard && editor.getSelection().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CopyOptions.forceCopyWithSyntaxHighlighting = true;
|
||||
super.run(accessor, editor);
|
||||
CopyOptions.forceCopyWithSyntaxHighlighting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ abstract class FoldingAction<T> extends EditorAction {
|
||||
if (!foldingController) {
|
||||
return;
|
||||
}
|
||||
this.reportTelemetry(accessor);
|
||||
this.reportTelemetry(accessor, editor);
|
||||
this.invoke(foldingController, editor, args);
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ export class DefinitionActionConfig {
|
||||
public openToSide = false,
|
||||
public openInPeek = false,
|
||||
public filterCurrent = true,
|
||||
public noResultsMessage = nls.localize('generic.noResults', "Could not find anything")
|
||||
public noResultsMessage = nls.localize('generic.noResults', "No definition found")
|
||||
) {
|
||||
//
|
||||
}
|
||||
|
||||
@@ -322,7 +322,7 @@ class MarkerNavigationAction extends EditorAction {
|
||||
}
|
||||
|
||||
let model = controller.getOrCreateModel();
|
||||
telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error' });
|
||||
telemetryService.publicLog('zoneWidgetShown', { mode: 'go to error', ...editor.getTelemetryData() });
|
||||
if (model) {
|
||||
if (this._isNext) {
|
||||
model.next();
|
||||
|
||||
@@ -23,6 +23,7 @@ import { TokenizationRegistry, LanguageIdentifier, FontStyle, StandardTokenType
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { findMatchingThemeRule } from 'vs/editor/electron-browser/textMate/TMHelper';
|
||||
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
@editorContribution
|
||||
class InspectTMScopesController extends Disposable implements IEditorContribution {
|
||||
@@ -114,8 +115,8 @@ interface IDecodedMetadata {
|
||||
languageIdentifier: LanguageIdentifier;
|
||||
tokenType: StandardTokenType;
|
||||
fontStyle: FontStyle;
|
||||
foreground: string;
|
||||
background: string;
|
||||
foreground: Color;
|
||||
background: Color;
|
||||
}
|
||||
|
||||
function renderTokenText(tokenText: string): string {
|
||||
@@ -237,8 +238,8 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget {
|
||||
result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${escape(metadata.languageIdentifier.language)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${this._tokenTypeToString(metadata.tokenType)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${this._fontStyleToString(metadata.fontStyle)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata.foreground}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata.background}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata.foreground.toRGBAHex()}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata.background.toRGBAHex()}</td>`;
|
||||
result += `</tbody></table>`;
|
||||
|
||||
let theme = this._themeService.getColorTheme();
|
||||
|
||||
@@ -20,6 +20,7 @@ import { CharCode } from 'vs/base/common/charCode';
|
||||
import { IStandaloneColorService } from 'vs/editor/common/services/standaloneColorService';
|
||||
import { NULL_STATE, nullTokenize, nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
import { Token } from 'vs/editor/common/core/token';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
@editorContribution
|
||||
class InspectTokensController extends Disposable implements IEditorContribution {
|
||||
@@ -109,8 +110,8 @@ interface IDecodedMetadata {
|
||||
languageIdentifier: LanguageIdentifier;
|
||||
tokenType: StandardTokenType;
|
||||
fontStyle: FontStyle;
|
||||
foreground: string;
|
||||
background: string;
|
||||
foreground: Color;
|
||||
background: Color;
|
||||
}
|
||||
|
||||
function renderTokenText(tokenText: string): string {
|
||||
@@ -235,8 +236,8 @@ class InspectTokensWidget extends Disposable implements IContentWidget {
|
||||
result += `<tr><td class="tm-metadata-key">language</td><td class="tm-metadata-value">${escape(metadata.languageIdentifier.language)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">token type</td><td class="tm-metadata-value">${this._tokenTypeToString(metadata.tokenType)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">font style</td><td class="tm-metadata-value">${this._fontStyleToString(metadata.fontStyle)}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata.foreground}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata.background}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">foreground</td><td class="tm-metadata-value">${metadata.foreground.toRGBHex()}</td>`;
|
||||
result += `<tr><td class="tm-metadata-key">background</td><td class="tm-metadata-value">${metadata.background.toRGBHex()}</td>`;
|
||||
result += `</tbody></table>`;
|
||||
|
||||
result += `<hr/>`;
|
||||
|
||||
@@ -593,7 +593,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
} else {
|
||||
const { stats } = this.completionModel;
|
||||
stats['wasAutomaticallyTriggered'] = !!isAuto;
|
||||
this.telemetryService.publicLog('suggestWidget', stats);
|
||||
this.telemetryService.publicLog('suggestWidget', { ...stats, ...this.editor.getTelemetryData() });
|
||||
|
||||
this.list.splice(0, this.list.length, this.completionModel.items);
|
||||
this.list.setFocus([this.completionModel.topScoreIdx]);
|
||||
@@ -694,7 +694,7 @@ export class SuggestWidget implements IContentWidget, IDelegate<ICompletionItem>
|
||||
|
||||
this.setState(State.Details);
|
||||
this.editor.focus();
|
||||
this.telemetryService.publicLog('suggestWidget:toggleDetails');
|
||||
this.telemetryService.publicLog('suggestWidget:toggleDetails', this.editor.getTelemetryData());
|
||||
}
|
||||
|
||||
private show(): void {
|
||||
|
||||
@@ -21,7 +21,8 @@ import { grammarsExtPoint, IEmbeddedLanguagesMap, ITMSyntaxExtensionPoint } from
|
||||
import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
|
||||
import { nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
import { hexToCSS } from 'vs/base/common/color';
|
||||
import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
export class TMScopeRegistry {
|
||||
|
||||
@@ -150,23 +151,19 @@ export class MainProcessTextMateSyntax implements ITextMateService {
|
||||
});
|
||||
}
|
||||
|
||||
private static _generateCSS(colorMap: string[]): string {
|
||||
let rules: string[] = [];
|
||||
private static _toColorMap(colorMap: string[]): Color[] {
|
||||
let result: Color[] = [null];
|
||||
for (let i = 1, len = colorMap.length; i < len; i++) {
|
||||
let color = colorMap[i];
|
||||
rules[i] = `.mtk${i} { color: ${hexToCSS(color)}; }`;
|
||||
result[i] = Color.fromHex(colorMap[i]);
|
||||
}
|
||||
rules.push('.mtki { font-style: italic; }');
|
||||
rules.push('.mtkb { font-weight: bold; }');
|
||||
rules.push('.mtku { text-decoration: underline; }');
|
||||
return rules.join('\n');
|
||||
return result;
|
||||
}
|
||||
|
||||
private _updateTheme(): void {
|
||||
let colorTheme = this._themeService.getColorTheme();
|
||||
this._grammarRegistry.setTheme({ name: colorTheme.label, settings: colorTheme.settings });
|
||||
let colorMap = this._grammarRegistry.getColorMap();
|
||||
let cssRules = MainProcessTextMateSyntax._generateCSS(colorMap);
|
||||
let colorMap = MainProcessTextMateSyntax._toColorMap(this._grammarRegistry.getColorMap());
|
||||
let cssRules = generateTokensCSSForColorMap(colorMap);
|
||||
this._styleElement.innerHTML = cssRules;
|
||||
TokenizationRegistry.setColorMap(colorMap);
|
||||
}
|
||||
|
||||
@@ -53,6 +53,14 @@ class SingleLineTestModel implements ISimpleModel {
|
||||
getLineCount(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
class TestView {
|
||||
@@ -180,4 +188,4 @@ const TESTS = [
|
||||
TESTS.forEach((t) => {
|
||||
document.body.appendChild(doCreateTest(TextAreaStrategy.NVDA, t.description, t.in, t.out));
|
||||
document.body.appendChild(doCreateTest(TextAreaStrategy.IENarrator, t.description, t.in, t.out));
|
||||
});
|
||||
});
|
||||
@@ -4,9 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Constants, MinimapCharRenderer } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
import { Constants, MinimapCharRenderer, ParsedColor } from 'vs/editor/common/view/minimapCharRenderer';
|
||||
import { MinimapCharRendererFactory } from 'vs/editor/test/common/view/minimapCharRendererFactory';
|
||||
import { createMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
|
||||
import { getOrCreateMinimapCharRenderer } from 'vs/editor/common/view/runtimeMinimapCharRenderer';
|
||||
|
||||
let canvas = <HTMLCanvasElement>document.getElementById('my-canvas');
|
||||
let ctx = canvas.getContext('2d');
|
||||
@@ -26,23 +26,55 @@ for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCo
|
||||
let sampleData = ctx.getImageData(0, 4, Constants.SAMPLED_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.SAMPLED_CHAR_HEIGHT);
|
||||
let minimapCharRenderer = MinimapCharRendererFactory.create(sampleData.data);
|
||||
|
||||
renderImageData(sampleData.data, sampleData.width, sampleData.height, 10, 100);
|
||||
renderImageData(sampleData, 10, 100);
|
||||
renderMinimapCharRenderer(minimapCharRenderer, 400);
|
||||
renderMinimapCharRenderer(createMinimapCharRenderer(), 600);
|
||||
renderMinimapCharRenderer(getOrCreateMinimapCharRenderer(), 600);
|
||||
|
||||
function createFakeImageData(width: number, height: number): ImageData {
|
||||
return {
|
||||
width: width,
|
||||
height: height,
|
||||
data: new Uint8ClampedArray(width * height * Constants.RGBA_CHANNELS_CNT)
|
||||
};
|
||||
}
|
||||
|
||||
function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y: number): void {
|
||||
|
||||
let x2 = new Uint8ClampedArray(Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
|
||||
for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) {
|
||||
minimapCharRenderer.x2RenderChar(x2, Constants.CHAR_COUNT, 0, chCode - Constants.START_CH_CODE, chCode);
|
||||
}
|
||||
renderImageData(x2, Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x2_CHAR_HEIGHT, 10, y);
|
||||
let background = new ParsedColor(0, 0, 0);
|
||||
let color = new ParsedColor(255, 255, 255);
|
||||
|
||||
let x1 = new Uint8ClampedArray(Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.RGBA_CHANNELS_CNT * Constants.CHAR_COUNT);
|
||||
for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) {
|
||||
minimapCharRenderer.x1RenderChar(x1, Constants.CHAR_COUNT, 0, chCode - Constants.START_CH_CODE, chCode);
|
||||
{
|
||||
let x2 = createFakeImageData(Constants.x2_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x2_CHAR_HEIGHT);
|
||||
// set the background color
|
||||
for (let i = 0, len = x2.data.length / 4; i < len; i++) {
|
||||
x2.data[4 * i + 0] = background.r;
|
||||
x2.data[4 * i + 1] = background.g;
|
||||
x2.data[4 * i + 2] = background.b;
|
||||
x2.data[4 * i + 3] = 255;
|
||||
}
|
||||
let dx = 0;
|
||||
for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) {
|
||||
minimapCharRenderer.x2RenderChar(x2, dx, 0, chCode, color, background);
|
||||
dx += Constants.x2_CHAR_WIDTH;
|
||||
}
|
||||
renderImageData(x2, 10, y);
|
||||
}
|
||||
{
|
||||
let x1 = createFakeImageData(Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x1_CHAR_HEIGHT);
|
||||
// set the background color
|
||||
for (let i = 0, len = x1.data.length / 4; i < len; i++) {
|
||||
x1.data[4 * i + 0] = background.r;
|
||||
x1.data[4 * i + 1] = background.g;
|
||||
x1.data[4 * i + 2] = background.b;
|
||||
x1.data[4 * i + 3] = 255;
|
||||
}
|
||||
let dx = 0;
|
||||
for (let chCode = Constants.START_CH_CODE; chCode <= Constants.END_CH_CODE; chCode++) {
|
||||
minimapCharRenderer.x1RenderChar(x1, dx, 0, chCode, color, background);
|
||||
dx += Constants.x1_CHAR_WIDTH;
|
||||
}
|
||||
renderImageData(x1, 10, y + 100);
|
||||
}
|
||||
renderImageData(x1, Constants.x1_CHAR_WIDTH * Constants.CHAR_COUNT, Constants.x1_CHAR_HEIGHT, 10, y + 100);
|
||||
}
|
||||
|
||||
(function () {
|
||||
@@ -51,8 +83,8 @@ function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y:
|
||||
let charCode = charIndex + Constants.START_CH_CODE;
|
||||
r += '\n\n// ' + String.fromCharCode(charCode);
|
||||
|
||||
for (let i = 0; i < Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH * Constants.CA_CHANNELS_CNT; i++) {
|
||||
if (i % 4 === 0) {
|
||||
for (let i = 0; i < Constants.x2_CHAR_HEIGHT * Constants.x2_CHAR_WIDTH; i++) {
|
||||
if (i % 2 === 0) {
|
||||
r += '\n';
|
||||
}
|
||||
r += minimapCharRenderer.x2charData[offset] + ',';
|
||||
@@ -70,10 +102,8 @@ function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y:
|
||||
let charCode = charIndex + Constants.START_CH_CODE;
|
||||
r += '\n\n// ' + String.fromCharCode(charCode);
|
||||
|
||||
for (let i = 0; i < Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH * Constants.CA_CHANNELS_CNT; i++) {
|
||||
if (i % 2 === 0) {
|
||||
r += '\n';
|
||||
}
|
||||
for (let i = 0; i < Constants.x1_CHAR_HEIGHT * Constants.x1_CHAR_WIDTH; i++) {
|
||||
r += '\n';
|
||||
r += minimapCharRenderer.x1charData[offset] + ',';
|
||||
offset++;
|
||||
}
|
||||
@@ -85,16 +115,16 @@ function renderMinimapCharRenderer(minimapCharRenderer: MinimapCharRenderer, y:
|
||||
|
||||
|
||||
|
||||
function renderImageData(data: Uint8ClampedArray, width: number, height: number, left: number, top: number): void {
|
||||
function renderImageData(imageData: ImageData, left: number, top: number): void {
|
||||
let output = '';
|
||||
var offset = 0;
|
||||
var PX_SIZE = 15;
|
||||
for (var i = 0; i < height; i++) {
|
||||
for (var j = 0; j < width; j++) {
|
||||
var R = data[offset];
|
||||
var G = data[offset + 1];
|
||||
var B = data[offset + 2];
|
||||
var A = data[offset + 3];
|
||||
for (var i = 0; i < imageData.height; i++) {
|
||||
for (var j = 0; j < imageData.width; j++) {
|
||||
var R = imageData.data[offset];
|
||||
var G = imageData.data[offset + 1];
|
||||
var B = imageData.data[offset + 2];
|
||||
var A = imageData.data[offset + 3];
|
||||
offset += 4;
|
||||
|
||||
output += `<div style="position:absolute;top:${PX_SIZE * i}px;left:${PX_SIZE * j}px;width:${PX_SIZE}px;height:${PX_SIZE}px;background:rgba(${R},${G},${B},${A / 256})"></div>`;
|
||||
@@ -105,8 +135,8 @@ function renderImageData(data: Uint8ClampedArray, width: number, height: number,
|
||||
domNode.style.position = 'absolute';
|
||||
domNode.style.top = top + 'px';
|
||||
domNode.style.left = left + 'px';
|
||||
domNode.style.width = (width * PX_SIZE) + 'px';
|
||||
domNode.style.height = (height * PX_SIZE) + 'px';
|
||||
domNode.style.width = (imageData.width * PX_SIZE) + 'px';
|
||||
domNode.style.height = (imageData.height * PX_SIZE) + 'px';
|
||||
domNode.style.border = '1px solid #ccc';
|
||||
domNode.style.background = '#000000';
|
||||
domNode.innerHTML = output;
|
||||
|
||||
@@ -910,6 +910,112 @@ suite('Editor Controller - Cursor', () => {
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('issue #20087: column select with mouse', () => {
|
||||
let model = Model.createFromString([
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" Key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SoMEKEy" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" valuE="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="00X"/>',
|
||||
].join('\n'));
|
||||
let cursor = new Cursor(new MockConfiguration(null), model, viewModelHelper(model), true);
|
||||
|
||||
moveTo(cursor, 10, 10, false);
|
||||
assertCursor(cursor, new Position(10, 10));
|
||||
|
||||
cursorCommand(cursor, H.ColumnSelect, {
|
||||
position: new Position(1, 1),
|
||||
viewPosition: new Position(1, 1),
|
||||
mouseColumn: 1
|
||||
});
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 1),
|
||||
new Selection(9, 10, 9, 1),
|
||||
new Selection(8, 10, 8, 1),
|
||||
new Selection(7, 10, 7, 1),
|
||||
new Selection(6, 10, 6, 1),
|
||||
new Selection(5, 10, 5, 1),
|
||||
new Selection(4, 10, 4, 1),
|
||||
new Selection(3, 10, 3, 1),
|
||||
new Selection(2, 10, 2, 1),
|
||||
new Selection(1, 10, 1, 1),
|
||||
]);
|
||||
|
||||
cursorCommand(cursor, H.ColumnSelect, {
|
||||
position: new Position(1, 1),
|
||||
viewPosition: new Position(1, 1),
|
||||
mouseColumn: 1
|
||||
});
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 1),
|
||||
new Selection(9, 10, 9, 1),
|
||||
new Selection(8, 10, 8, 1),
|
||||
new Selection(7, 10, 7, 1),
|
||||
new Selection(6, 10, 6, 1),
|
||||
new Selection(5, 10, 5, 1),
|
||||
new Selection(4, 10, 4, 1),
|
||||
new Selection(3, 10, 3, 1),
|
||||
new Selection(2, 10, 2, 1),
|
||||
new Selection(1, 10, 1, 1),
|
||||
]);
|
||||
|
||||
cursor.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('issue #20087: column select with keyboard', () => {
|
||||
let model = Model.createFromString([
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" Key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SoMEKEy" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" valuE="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="000"/>',
|
||||
'<property id="SomeThing" key="SomeKey" value="00X"/>',
|
||||
].join('\n'));
|
||||
let cursor = new Cursor(new MockConfiguration(null), model, viewModelHelper(model), true);
|
||||
|
||||
moveTo(cursor, 10, 10, false);
|
||||
assertCursor(cursor, new Position(10, 10));
|
||||
|
||||
cursorCommand(cursor, H.CursorColumnSelectLeft);
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 9)
|
||||
]);
|
||||
|
||||
cursorCommand(cursor, H.CursorColumnSelectLeft);
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 8)
|
||||
]);
|
||||
|
||||
cursorCommand(cursor, H.CursorColumnSelectRight);
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 9)
|
||||
]);
|
||||
|
||||
cursorCommand(cursor, H.CursorColumnSelectUp);
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 9),
|
||||
new Selection(9, 10, 9, 9),
|
||||
]);
|
||||
|
||||
cursorCommand(cursor, H.CursorColumnSelectDown);
|
||||
assertCursor(cursor, [
|
||||
new Selection(10, 10, 10, 9)
|
||||
]);
|
||||
|
||||
cursor.dispose();
|
||||
model.dispose();
|
||||
});
|
||||
|
||||
test('column select with keyboard', () => {
|
||||
let model = Model.createFromString([
|
||||
'var gulp = require("gulp");',
|
||||
|
||||
@@ -472,4 +472,12 @@ class SimpleModel implements ISimpleModel {
|
||||
public getLineCount(): number {
|
||||
return this._lines.length;
|
||||
}
|
||||
|
||||
public getPlainTextToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public getHTMLToCopy(ranges: Range[], enableEmptySelectionClipboard: boolean): string {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,292 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { MetadataConsts } from 'vs/editor/common/modes';
|
||||
import { ViewLineToken } from 'vs/editor/common/core/viewLineToken';
|
||||
|
||||
suite('LineTokens', () => {
|
||||
|
||||
interface ILineToken {
|
||||
startIndex: number;
|
||||
foreground: number;
|
||||
}
|
||||
|
||||
function createLineTokens(text: string, tokens: ILineToken[]): LineTokens {
|
||||
let binTokens = new Uint32Array(tokens.length << 1);
|
||||
|
||||
for (let i = 0, len = tokens.length; i < len; i++) {
|
||||
let token = tokens[i];
|
||||
binTokens[(i << 1)] = token.startIndex;
|
||||
binTokens[(i << 1) + 1] = (
|
||||
token.foreground << MetadataConsts.FOREGROUND_OFFSET
|
||||
) >>> 0;
|
||||
}
|
||||
|
||||
return new LineTokens(binTokens, text);
|
||||
}
|
||||
|
||||
function createTestLineTokens(): LineTokens {
|
||||
return createLineTokens(
|
||||
'Hello world, this is a lovely day',
|
||||
[
|
||||
{ startIndex: 0, foreground: 1 }, // Hello_
|
||||
{ startIndex: 6, foreground: 2 }, // world,_
|
||||
{ startIndex: 13, foreground: 3 }, // this_
|
||||
{ startIndex: 18, foreground: 4 }, // is_
|
||||
{ startIndex: 21, foreground: 5 }, // a_
|
||||
{ startIndex: 23, foreground: 6 }, // lovely_
|
||||
{ startIndex: 30, foreground: 7 }, // day
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
test('basics', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
|
||||
assert.equal(lineTokens.getLineContent(), 'Hello world, this is a lovely day');
|
||||
assert.equal(lineTokens.getLineLength(), 33);
|
||||
assert.equal(lineTokens.getTokenCount(), 7);
|
||||
|
||||
assert.equal(lineTokens.getTokenStartOffset(0), 0);
|
||||
assert.equal(lineTokens.getTokenEndOffset(0), 6);
|
||||
assert.equal(lineTokens.getTokenStartOffset(1), 6);
|
||||
assert.equal(lineTokens.getTokenEndOffset(1), 13);
|
||||
assert.equal(lineTokens.getTokenStartOffset(2), 13);
|
||||
assert.equal(lineTokens.getTokenEndOffset(2), 18);
|
||||
assert.equal(lineTokens.getTokenStartOffset(3), 18);
|
||||
assert.equal(lineTokens.getTokenEndOffset(3), 21);
|
||||
assert.equal(lineTokens.getTokenStartOffset(4), 21);
|
||||
assert.equal(lineTokens.getTokenEndOffset(4), 23);
|
||||
assert.equal(lineTokens.getTokenStartOffset(5), 23);
|
||||
assert.equal(lineTokens.getTokenEndOffset(5), 30);
|
||||
assert.equal(lineTokens.getTokenStartOffset(6), 30);
|
||||
assert.equal(lineTokens.getTokenEndOffset(6), 33);
|
||||
});
|
||||
|
||||
test('findToken', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(0), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(1), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(2), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(3), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(4), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(5), 0);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(6), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(7), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(8), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(9), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(10), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(11), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(12), 1);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(13), 2);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(14), 2);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(15), 2);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(16), 2);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(17), 2);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(18), 3);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(19), 3);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(20), 3);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(21), 4);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(22), 4);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(23), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(24), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(25), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(26), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(27), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(28), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(29), 5);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(30), 6);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(31), 6);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(32), 6);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(33), 6);
|
||||
assert.equal(lineTokens.findTokenIndexAtOffset(34), 6);
|
||||
|
||||
assert.equal(lineTokens.findTokenAtOffset(7).startOffset, 6);
|
||||
assert.equal(lineTokens.findTokenAtOffset(7).endOffset, 13);
|
||||
assert.equal(lineTokens.findTokenAtOffset(7).foregroundId, 2);
|
||||
|
||||
assert.equal(lineTokens.findTokenAtOffset(30).startOffset, 30);
|
||||
assert.equal(lineTokens.findTokenAtOffset(30).endOffset, 33);
|
||||
assert.equal(lineTokens.findTokenAtOffset(30).foregroundId, 7);
|
||||
});
|
||||
|
||||
test('iterate forward', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
|
||||
let token = lineTokens.firstToken();
|
||||
assert.equal(token.startOffset, 0);
|
||||
assert.equal(token.endOffset, 6);
|
||||
assert.equal(token.foregroundId, 1);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 6);
|
||||
assert.equal(token.endOffset, 13);
|
||||
assert.equal(token.foregroundId, 2);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 13);
|
||||
assert.equal(token.endOffset, 18);
|
||||
assert.equal(token.foregroundId, 3);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 18);
|
||||
assert.equal(token.endOffset, 21);
|
||||
assert.equal(token.foregroundId, 4);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 21);
|
||||
assert.equal(token.endOffset, 23);
|
||||
assert.equal(token.foregroundId, 5);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 23);
|
||||
assert.equal(token.endOffset, 30);
|
||||
assert.equal(token.foregroundId, 6);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token.startOffset, 30);
|
||||
assert.equal(token.endOffset, 33);
|
||||
assert.equal(token.foregroundId, 7);
|
||||
|
||||
token = token.next();
|
||||
assert.equal(token, null);
|
||||
});
|
||||
|
||||
test('iterate backward', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
|
||||
let token = lineTokens.lastToken();
|
||||
assert.equal(token.startOffset, 30);
|
||||
assert.equal(token.endOffset, 33);
|
||||
assert.equal(token.foregroundId, 7);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 23);
|
||||
assert.equal(token.endOffset, 30);
|
||||
assert.equal(token.foregroundId, 6);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 21);
|
||||
assert.equal(token.endOffset, 23);
|
||||
assert.equal(token.foregroundId, 5);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 18);
|
||||
assert.equal(token.endOffset, 21);
|
||||
assert.equal(token.foregroundId, 4);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 13);
|
||||
assert.equal(token.endOffset, 18);
|
||||
assert.equal(token.foregroundId, 3);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 6);
|
||||
assert.equal(token.endOffset, 13);
|
||||
assert.equal(token.foregroundId, 2);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token.startOffset, 0);
|
||||
assert.equal(token.endOffset, 6);
|
||||
assert.equal(token.foregroundId, 1);
|
||||
|
||||
token = token.prev();
|
||||
assert.equal(token, null);
|
||||
});
|
||||
|
||||
interface ITestViewLineToken {
|
||||
endIndex: number;
|
||||
foreground: number;
|
||||
}
|
||||
|
||||
function assertViewLineTokens(actual: ViewLineToken[], expected: ITestViewLineToken[]): void {
|
||||
assert.deepEqual(actual.map(token => {
|
||||
return {
|
||||
endIndex: token.endIndex,
|
||||
foreground: token.getForeground()
|
||||
};
|
||||
}), expected);
|
||||
}
|
||||
|
||||
test('inflate', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
assertViewLineTokens(lineTokens.inflate(), [
|
||||
{ endIndex: 6, foreground: 1 },
|
||||
{ endIndex: 13, foreground: 2 },
|
||||
{ endIndex: 18, foreground: 3 },
|
||||
{ endIndex: 21, foreground: 4 },
|
||||
{ endIndex: 23, foreground: 5 },
|
||||
{ endIndex: 30, foreground: 6 },
|
||||
{ endIndex: 33, foreground: 7 },
|
||||
]);
|
||||
});
|
||||
|
||||
test('sliceAndInflate', () => {
|
||||
const lineTokens = createTestLineTokens();
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(0, 33, 0), [
|
||||
{ endIndex: 6, foreground: 1 },
|
||||
{ endIndex: 13, foreground: 2 },
|
||||
{ endIndex: 18, foreground: 3 },
|
||||
{ endIndex: 21, foreground: 4 },
|
||||
{ endIndex: 23, foreground: 5 },
|
||||
{ endIndex: 30, foreground: 6 },
|
||||
{ endIndex: 33, foreground: 7 },
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(0, 32, 0), [
|
||||
{ endIndex: 6, foreground: 1 },
|
||||
{ endIndex: 13, foreground: 2 },
|
||||
{ endIndex: 18, foreground: 3 },
|
||||
{ endIndex: 21, foreground: 4 },
|
||||
{ endIndex: 23, foreground: 5 },
|
||||
{ endIndex: 30, foreground: 6 },
|
||||
{ endIndex: 32, foreground: 7 },
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 0), [
|
||||
{ endIndex: 6, foreground: 1 },
|
||||
{ endIndex: 13, foreground: 2 },
|
||||
{ endIndex: 18, foreground: 3 },
|
||||
{ endIndex: 21, foreground: 4 },
|
||||
{ endIndex: 23, foreground: 5 },
|
||||
{ endIndex: 30, foreground: 6 }
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(0, 30, 1), [
|
||||
{ endIndex: 7, foreground: 1 },
|
||||
{ endIndex: 14, foreground: 2 },
|
||||
{ endIndex: 19, foreground: 3 },
|
||||
{ endIndex: 22, foreground: 4 },
|
||||
{ endIndex: 24, foreground: 5 },
|
||||
{ endIndex: 31, foreground: 6 }
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(6, 18, 0), [
|
||||
{ endIndex: 7, foreground: 2 },
|
||||
{ endIndex: 12, foreground: 3 }
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(7, 18, 0), [
|
||||
{ endIndex: 6, foreground: 2 },
|
||||
{ endIndex: 11, foreground: 3 }
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(6, 17, 0), [
|
||||
{ endIndex: 7, foreground: 2 },
|
||||
{ endIndex: 11, foreground: 3 }
|
||||
]);
|
||||
|
||||
assertViewLineTokens(lineTokens.sliceAndInflate(6, 19, 0), [
|
||||
{ endIndex: 7, foreground: 2 },
|
||||
{ endIndex: 12, foreground: 3 },
|
||||
{ endIndex: 13, foreground: 4 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -30,6 +30,10 @@ export class MockConfiguration extends CommonEditorConfiguration {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected _getPixelRatio(): number {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected readConfiguration(styling: BareFontInfo): FontInfo {
|
||||
return new FontInfo({
|
||||
fontFamily: 'mockFont',
|
||||
|
||||
@@ -9,12 +9,18 @@ import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { ModelLine, ILineEdit, LineMarker, MarkersTracker } from 'vs/editor/common/model/modelLine';
|
||||
import { MetadataConsts } from 'vs/editor/common/modes';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { TokenMetadata } from 'vs/editor/common/model/tokensBinaryEncoding';
|
||||
import { ViewLineToken, ViewLineTokenFactory } from 'vs/editor/common/core/viewLineToken';
|
||||
|
||||
function assertLineTokens(_actual: LineTokens, _expected: TestToken[]): void {
|
||||
let expected = TokenMetadata.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength());
|
||||
let expected = ViewLineTokenFactory.inflateArr(TestToken.toTokens(_expected), _actual.getLineLength());
|
||||
let actual = _actual.inflate();
|
||||
assert.deepEqual(actual, expected);
|
||||
let decode = (token: ViewLineToken) => {
|
||||
return {
|
||||
endIndex: token.endIndex,
|
||||
type: token.getType()
|
||||
};
|
||||
};
|
||||
assert.deepEqual(actual.map(decode), expected.map(decode));
|
||||
}
|
||||
|
||||
const NO_TAB_SIZE = 0;
|
||||
@@ -298,7 +304,7 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => {
|
||||
line.applyEdits(new MarkersTracker(), edits, NO_TAB_SIZE);
|
||||
|
||||
assert.equal(line.text, expectedText);
|
||||
assertLineTokens(line.getTokens(0, []), expectedTokens);
|
||||
assertLineTokens(line.getTokens(0), expectedTokens);
|
||||
}
|
||||
|
||||
test('insertion on empty line', () => {
|
||||
@@ -309,7 +315,7 @@ suite('Editor Model - modelLine.applyEdits text & tokens', () => {
|
||||
line.setTokens(0, new Uint32Array(0));
|
||||
|
||||
line.applyEdits(new MarkersTracker(), [{ startColumn: 1, endColumn: 1, text: 'a', forceMoveMarkers: false }], NO_TAB_SIZE);
|
||||
assertLineTokens(line.getTokens(0, []), [new TestToken(0, 1)]);
|
||||
assertLineTokens(line.getTokens(0), [new TestToken(0, 1)]);
|
||||
});
|
||||
|
||||
test('updates tokens on insertion 1', () => {
|
||||
@@ -871,7 +877,7 @@ suite('Editor Model - modelLine.split text & tokens', () => {
|
||||
|
||||
assert.equal(line.text, expectedText1);
|
||||
assert.equal(other.text, expectedText2);
|
||||
assertLineTokens(line.getTokens(0, []), expectedTokens);
|
||||
assertLineTokens(line.getTokens(0), expectedTokens);
|
||||
}
|
||||
|
||||
test('split at the beginning', () => {
|
||||
@@ -957,7 +963,7 @@ suite('Editor Model - modelLine.append text & tokens', () => {
|
||||
a.append(new MarkersTracker(), b, NO_TAB_SIZE);
|
||||
|
||||
assert.equal(a.text, expectedText);
|
||||
assertLineTokens(a.getTokens(0, []), expectedTokens);
|
||||
assertLineTokens(a.getTokens(0), expectedTokens);
|
||||
}
|
||||
|
||||
test('append empty 1', () => {
|
||||
|
||||
@@ -265,7 +265,13 @@ suite('TextModelWithTokens regression tests', () => {
|
||||
test('Microsoft/monaco-editor#122: Unhandled Exception: TypeError: Unable to get property \'replace\' of undefined or null reference', () => {
|
||||
function assertViewLineTokens(model: Model, lineNumber: number, forceTokenization: boolean, expected: ViewLineToken[]): void {
|
||||
let actual = model.getLineTokens(lineNumber, !forceTokenization).inflate();
|
||||
assert.deepEqual(actual, expected);
|
||||
let decode = (token: ViewLineToken) => {
|
||||
return {
|
||||
endIndex: token.endIndex,
|
||||
foreground: token.getForeground()
|
||||
};
|
||||
};
|
||||
assert.deepEqual(actual.map(decode), expected.map(decode));
|
||||
}
|
||||
|
||||
let _tokenId = 10;
|
||||
@@ -293,24 +299,32 @@ suite('TextModelWithTokens regression tests', () => {
|
||||
|
||||
let model = Model.createFromString('A model with\ntwo lines');
|
||||
|
||||
assertViewLineTokens(model, 1, true, [new ViewLineToken(12, 'mtk1')]);
|
||||
assertViewLineTokens(model, 2, true, [new ViewLineToken(9, 'mtk1')]);
|
||||
assertViewLineTokens(model, 1, true, [createViewLineToken(12, 1)]);
|
||||
assertViewLineTokens(model, 2, true, [createViewLineToken(9, 1)]);
|
||||
|
||||
model.setMode(languageIdentifier1);
|
||||
|
||||
assertViewLineTokens(model, 1, true, [new ViewLineToken(12, 'mtk11')]);
|
||||
assertViewLineTokens(model, 2, true, [new ViewLineToken(9, 'mtk12')]);
|
||||
assertViewLineTokens(model, 1, true, [createViewLineToken(12, 11)]);
|
||||
assertViewLineTokens(model, 2, true, [createViewLineToken(9, 12)]);
|
||||
|
||||
model.setMode(languageIdentifier2);
|
||||
|
||||
assertViewLineTokens(model, 1, false, [new ViewLineToken(12, 'mtk1')]);
|
||||
assertViewLineTokens(model, 2, false, [new ViewLineToken(9, 'mtk1')]);
|
||||
assertViewLineTokens(model, 1, false, [createViewLineToken(12, 1)]);
|
||||
assertViewLineTokens(model, 2, false, [createViewLineToken(9, 1)]);
|
||||
|
||||
model.dispose();
|
||||
registration1.dispose();
|
||||
registration2.dispose();
|
||||
|
||||
function createViewLineToken(endIndex: number, foreground: number): ViewLineToken {
|
||||
let metadata = (
|
||||
(foreground << MetadataConsts.FOREGROUND_OFFSET)
|
||||
) >>> 0;
|
||||
return new ViewLineToken(endIndex, metadata);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
test('Microsoft/monaco-editor#133: Error: Cannot read property \'modeId\' of undefined', () => {
|
||||
|
||||
const languageIdentifier = new LanguageIdentifier('testMode', LanguageId.PlainText);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user