From 52ac8ab36774c96fb672c3ca93ad5d490e53cd70 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 25 Aug 2017 11:44:54 +0200 Subject: [PATCH] Add MarkdownString to Hover and Decorations API, #29076 --- src/vs/vscode.d.ts | 47 +++++++++++++++++-- src/vs/workbench/api/node/extHost.api.impl.ts | 2 + .../api/node/extHostTypeConverters.ts | 31 +++++++----- src/vs/workbench/api/node/extHostTypes.ts | 15 ++++-- 4 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index c08aceb3d2c..b0b49c09ea4 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -1818,11 +1818,50 @@ declare module 'vscode' { } /** - * MarkedString can be used to render human readable text. It is either a markdown string - * or a code-block that provides a language and a code snippet. Note that - * markdown strings will be sanitized - that means html will be escaped. + * The MarkdownString represents human readable text that supports formatting via the + * markdown syntax. Standard markdown is supported, also tables, but no embedded html. */ - export type MarkedString = string | { language: string; value: string }; + export class MarkdownString { + + /** + * The markdown string. + */ + value: string; + + /** + * Indicates that this markdown string is from a trusted source. Only *trusted* + * markdown supports links that execute commands, e.g. `[Run it](command:myCommandId)`. + */ + isTrusted?: boolean; + + /** + * Creates a new markdown string with the given value. + * + * @param value Optional, initial value. + */ + constructor(value?: string); + + /** + * Appends and escapes the given string to this markdown string. + * @param value Plain text. + */ + appendText(value: string): MarkdownString; + + /** + * Appends the given string 'as is' to this markdown string. + * @param value Markdown string. + */ + appendMarkdown(value: string): MarkdownString; + } + + /** + * ~~MarkedString can be used to render human readable text. It is either a markdown string + * or a code-block that provides a language and a code snippet. Note that + * markdown strings will be sanitized - that means html will be escaped.~~ + * + * @deprecated This type is deprecated, please use [`MarkdownString`](#MarkdownString) instead. + */ + export type MarkedString = MarkdownString | string | { language: string; value: string }; /** * A hover represents additional information for a symbol or word. Hovers are diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index bfd4b85fb69..68dc2526f7e 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -52,6 +52,7 @@ import { TextEditorCursorStyle } from 'vs/editor/common/config/editorOptions'; import { ExtHostThreadService } from 'vs/workbench/services/thread/node/extHostThreadService'; import { ProxyIdentifier } from 'vs/workbench/services/thread/common/threadService'; import { ExtHostDialogs } from 'vs/workbench/api/node/extHostDialogs'; +import { MarkdownString } from 'vs/base/common/htmlContent'; export interface IExtensionApiFactory { (extension: IExtensionDescription): typeof vscode; @@ -554,6 +555,7 @@ export function createApiFactory( Hover: extHostTypes.Hover, IndentAction: languageConfiguration.IndentAction, Location: extHostTypes.Location, + MarkdownString: MarkdownString, OverviewRulerLane: EditorCommon.OverviewRulerLane, ParameterInformation: extHostTypes.ParameterInformation, Position: extHostTypes.Position, diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index c1a046cecec..e7dde8f57a5 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -16,7 +16,7 @@ import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { IPosition } from 'vs/editor/common/core/position'; import { IRange } from 'vs/editor/common/core/range'; import { ISelection } from 'vs/editor/common/core/selection'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; +import * as htmlContent from 'vs/base/common/htmlContent'; export interface PositionLike { line: number; @@ -144,26 +144,35 @@ function isDecorationOptionsArr(something: vscode.Range[] | vscode.DecorationOpt return isDecorationOptions(something[0]) ? true : false; } -export namespace MarkedString { - export function from(markup: vscode.MarkedString): IMarkdownString { - if (typeof markup === 'string' || !markup) { - return { value: markup || '', trusted: true }; +export namespace MarkdownString { + + export function fromMany(markup: (vscode.MarkdownString | vscode.MarkedString)[]): htmlContent.IMarkdownString[] { + return markup.map(MarkdownString.from); + } + + export function from(markup: vscode.MarkdownString | vscode.MarkedString): htmlContent.IMarkdownString { + if (htmlContent.isMarkdownString(markup)) { + return markup; + } else if (typeof markup === 'string' || !markup) { + return { value: markup || '', isTrusted: true }; } else { const { language, value } = markup; return { value: '```' + language + '\n' + value + '\n```' }; } } - export function to(value: IMarkdownString): vscode.MarkedString { - return value.value; + export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { + const ret = new htmlContent.MarkdownString(value.value); + ret.isTrusted = value.isTrusted; + return ret; } } export function fromRangeOrRangeWithMessage(ranges: vscode.Range[] | vscode.DecorationOptions[]): IDecorationOptions[] { if (isDecorationOptionsArr(ranges)) { - return ranges.map((r): IDecorationOptions => { + return ranges.map(r => { return { range: fromRange(r.range), - hoverMessage: Array.isArray(r.hoverMessage) ? r.hoverMessage.map(MarkedString.from) : MarkedString.from(r.hoverMessage), + hoverMessage: Array.isArray(r.hoverMessage) ? MarkdownString.fromMany(r.hoverMessage) : r.hoverMessage && MarkdownString.from(r.hoverMessage), renderOptions: /* URI vs Uri */r.renderOptions }; }); @@ -271,12 +280,12 @@ export const location = { export function fromHover(hover: vscode.Hover): modes.Hover { return { range: fromRange(hover.range), - contents: hover.contents.map(MarkedString.from) + contents: MarkdownString.fromMany(hover.contents) }; } export function toHover(info: modes.Hover): types.Hover { - return new types.Hover(info.contents.map(MarkedString.to), toRange(info.range)); + return new types.Hover(info.contents.map(MarkdownString.to), toRange(info.range)); } export function toDocumentHighlight(occurrence: modes.DocumentHighlight): types.DocumentHighlight { diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index d154a8be24d..980d89b0c0e 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -10,6 +10,7 @@ import URI from 'vs/base/common/uri'; import { Color as BaseColor, HSLA } from 'vs/base/common/color'; import { illegalArgument } from 'vs/base/common/errors'; import * as vscode from 'vscode'; +import { isMarkdownString } from 'vs/base/common/htmlContent'; export class Disposable { @@ -698,16 +699,20 @@ export class Diagnostic { export class Hover { - public contents: vscode.MarkedString[]; + public contents: vscode.MarkdownString[] | vscode.MarkedString[]; public range: Range; - constructor(contents: vscode.MarkedString | vscode.MarkedString[], range?: Range) { + constructor( + contents: vscode.MarkdownString | vscode.MarkedString | vscode.MarkdownString[] | vscode.MarkedString[], + range?: Range + ) { if (!contents) { throw new Error('Illegal argument, contents must be defined'); } - if (Array.isArray(contents)) { - this.contents = contents; + this.contents = contents; + } else if (isMarkdownString(contents)) { + this.contents = [contents]; } else { this.contents = [contents]; } @@ -1377,4 +1382,4 @@ export enum ConfigurationTarget { Workspace = 2, WorkspaceFolder = 3 -} \ No newline at end of file +}