Files
azuredatastudio/extensions/markdown/src/commands.ts
Karl Burtram 251ae01c3e Initial VS Code 1.19 source merge (#571)
* Initial 1.19 xcopy

* Fix yarn build

* Fix numerous build breaks

* Next batch of build break fixes

* More build break fixes

* Runtime breaks

* Additional post merge fixes

* Fix windows setup file

* Fix test failures.

* Update license header blocks to refer to source eula
2018-01-28 23:37:17 -08:00

292 lines
9.2 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vscode-nls';
const localize = nls.config(process.env.VSCODE_NLS_CONFIG)();
import * as vscode from 'vscode';
import * as path from 'path';
import { Command } from './commandManager';
import { ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './security';
import { getMarkdownUri, MDDocumentContentProvider, isMarkdownFile } from './features/previewContentProvider';
import { Logger } from './logger';
import { TableOfContentsProvider } from './tableOfContentsProvider';
import { MarkdownEngine } from './markdownEngine';
import { TelemetryReporter } from './telemetryReporter';
function getViewColumn(sideBySide: boolean): vscode.ViewColumn | undefined {
const active = vscode.window.activeTextEditor;
if (!active) {
return vscode.ViewColumn.One;
}
if (!sideBySide) {
return active.viewColumn;
}
switch (active.viewColumn) {
case vscode.ViewColumn.One:
return vscode.ViewColumn.Two;
case vscode.ViewColumn.Two:
return vscode.ViewColumn.Three;
}
return active.viewColumn;
}
function showPreview(
cspArbiter: ExtensionContentSecurityPolicyArbiter,
telemetryReporter: TelemetryReporter,
uri?: vscode.Uri,
sideBySide: boolean = false,
) {
let resource = uri;
if (!(resource instanceof vscode.Uri)) {
if (vscode.window.activeTextEditor) {
// we are relaxed and don't check for markdown files
resource = vscode.window.activeTextEditor.document.uri;
}
}
if (!(resource instanceof vscode.Uri)) {
if (!vscode.window.activeTextEditor) {
// this is most likely toggling the preview
return vscode.commands.executeCommand('markdown.showSource');
}
// nothing found that could be shown or toggled
return;
}
const thenable = vscode.commands.executeCommand('vscode.previewHtml',
getMarkdownUri(resource),
getViewColumn(sideBySide),
localize('previewTitle', 'Preview {0}', path.basename(resource.fsPath)),
{
allowScripts: true,
allowSvgs: cspArbiter.shouldAllowSvgsForResource(resource)
});
telemetryReporter.sendTelemetryEvent('openPreview', {
where: sideBySide ? 'sideBySide' : 'inPlace',
how: (uri instanceof vscode.Uri) ? 'action' : 'pallete'
});
return thenable;
}
export class ShowPreviewCommand implements Command {
public readonly id = 'markdown.showPreview';
public constructor(
private readonly cspArbiter: ExtensionContentSecurityPolicyArbiter,
private readonly telemetryReporter: TelemetryReporter
) { }
public execute(uri?: vscode.Uri) {
showPreview(this.cspArbiter, this.telemetryReporter, uri, false);
}
}
export class ShowPreviewToSideCommand implements Command {
public readonly id = 'markdown.showPreviewToSide';
public constructor(
private readonly cspArbiter: ExtensionContentSecurityPolicyArbiter,
private readonly telemetryReporter: TelemetryReporter
) { }
public execute(uri?: vscode.Uri) {
showPreview(this.cspArbiter, this.telemetryReporter, uri, true);
}
}
export class ShowSourceCommand implements Command {
public readonly id = 'markdown.showSource';
public execute(mdUri?: vscode.Uri) {
if (!mdUri) {
return vscode.commands.executeCommand('workbench.action.navigateBack');
}
const docUri = vscode.Uri.parse(mdUri.query);
for (const editor of vscode.window.visibleTextEditors) {
if (editor.document.uri.scheme === docUri.scheme && editor.document.uri.toString() === docUri.toString()) {
return vscode.window.showTextDocument(editor.document, editor.viewColumn);
}
}
return vscode.workspace.openTextDocument(docUri)
.then(vscode.window.showTextDocument);
}
}
export class RefreshPreviewCommand implements Command {
public readonly id = 'markdown.refreshPreview';
public constructor(
private readonly contentProvider: MDDocumentContentProvider
) { }
public execute(resource: string | undefined) {
if (resource) {
const source = vscode.Uri.parse(resource);
this.contentProvider.update(source);
} else if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document)) {
this.contentProvider.update(getMarkdownUri(vscode.window.activeTextEditor.document.uri));
} else {
// update all generated md documents
for (const document of vscode.workspace.textDocuments) {
if (document.uri.scheme === 'markdown') {
this.contentProvider.update(document.uri);
}
}
}
}
}
export class ShowPreviewSecuritySelectorCommand implements Command {
public readonly id = 'markdown.showPreviewSecuritySelector';
public constructor(
private readonly previewSecuritySelector: PreviewSecuritySelector
) { }
public execute(resource: string | undefined) {
if (resource) {
const source = vscode.Uri.parse(resource).query;
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.Uri.parse(source));
} else {
if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.languageId === 'markdown') {
this.previewSecuritySelector.showSecutitySelectorForResource(vscode.window.activeTextEditor.document.uri);
}
}
}
}
export class RevealLineCommand implements Command {
public readonly id = '_markdown.revealLine';
public constructor(
private logger: Logger
) { }
public execute(uri: string, line: number) {
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
this.logger.log('revealLine', { uri, sourceUri: sourceUri.toString(), line });
vscode.window.visibleTextEditors
.filter(editor => isMarkdownFile(editor.document) && editor.document.uri.toString() === sourceUri.toString())
.forEach(editor => {
const sourceLine = Math.floor(line);
const fraction = line - sourceLine;
const text = editor.document.lineAt(sourceLine).text;
const start = Math.floor(fraction * text.length);
editor.revealRange(
new vscode.Range(sourceLine, start, sourceLine + 1, 0),
vscode.TextEditorRevealType.AtTop);
});
}
}
export class DidClickCommand implements Command {
public readonly id = '_markdown.didClick';
public execute(uri: string, line: number) {
const sourceUri = vscode.Uri.parse(decodeURIComponent(uri));
return vscode.workspace.openTextDocument(sourceUri)
.then(document => vscode.window.showTextDocument(document))
.then(editor =>
vscode.commands.executeCommand('revealLine', { lineNumber: Math.floor(line), at: 'center' })
.then(() => editor))
.then(editor => {
if (editor) {
editor.selection = new vscode.Selection(
new vscode.Position(Math.floor(line), 0),
new vscode.Position(Math.floor(line), 0));
}
});
}
}
export class MoveCursorToPositionCommand implements Command {
public readonly id = '_markdown.moveCursorToPosition';
public execute(line: number, character: number) {
if (!vscode.window.activeTextEditor) {
return;
}
const position = new vscode.Position(line, character);
const selection = new vscode.Selection(position, position);
vscode.window.activeTextEditor.revealRange(selection);
vscode.window.activeTextEditor.selection = selection;
}
}
export class OnPreviewStyleLoadErrorCommand implements Command {
public readonly id = '_markdown.onPreviewStyleLoadError';
public execute(resources: string[]) {
vscode.window.showWarningMessage(localize('onPreviewStyleLoadError', "Could not load 'markdown.styles': {0}", resources.join(', ')));
}
}
export interface OpenDocumentLinkArgs {
path: string;
fragment: string;
}
export class OpenDocumentLinkCommand implements Command {
private static readonly id = '_markdown.openDocumentLink';
public readonly id = OpenDocumentLinkCommand.id;
public static createCommandUri(
path: string,
fragment: string
): vscode.Uri {
return vscode.Uri.parse(`command:${OpenDocumentLinkCommand.id}?${encodeURIComponent(JSON.stringify({ path, fragment }))}`);
}
public constructor(
private readonly engine: MarkdownEngine
) { }
public execute(args: OpenDocumentLinkArgs) {
const tryRevealLine = async (editor: vscode.TextEditor) => {
if (editor && args.fragment) {
const toc = new TableOfContentsProvider(this.engine, editor.document);
const line = await toc.lookup(args.fragment);
if (!isNaN(line)) {
return editor.revealRange(
new vscode.Range(line, 0, line, 0),
vscode.TextEditorRevealType.AtTop);
}
}
};
const tryOpen = async (path: string) => {
if (vscode.window.activeTextEditor && isMarkdownFile(vscode.window.activeTextEditor.document) && vscode.window.activeTextEditor.document.uri.fsPath === path) {
return tryRevealLine(vscode.window.activeTextEditor);
} else {
const resource = vscode.Uri.file(path);
return vscode.workspace.openTextDocument(resource)
.then(vscode.window.showTextDocument)
.then(tryRevealLine);
}
};
return tryOpen(args.path).catch(() => {
if (path.extname(args.path) === '') {
return tryOpen(args.path + '.md');
}
const resource = vscode.Uri.file(args.path);
return Promise.resolve(void 0)
.then(() => vscode.commands.executeCommand('vscode.open', resource))
.then(() => void 0);
});
}
}