diff --git a/pkg/nuclide-datatip/lib/DatatipComponent.js b/pkg/nuclide-datatip/lib/DatatipComponent.js
index dcca69f063..7b128b8aa1 100644
--- a/pkg/nuclide-datatip/lib/DatatipComponent.js
+++ b/pkg/nuclide-datatip/lib/DatatipComponent.js
@@ -12,6 +12,7 @@ import type {Datatip} from './types';
import React from 'react';
import {maybeToString} from '../../commons-node/string';
+import MarkedStringDatatip from './MarkedStringDatatip';
export const DATATIP_ACTIONS = Object.freeze({
PIN: 'PIN',
@@ -63,8 +64,12 @@ export class DatatipComponent extends React.Component {
/>
);
}
- const ProvidedComponent = datatip.component;
- const content = ;
+ let content;
+ if (datatip.component != null) {
+ content = ;
+ } else if (datatip.markedStrings != null) {
+ content = ;
+ }
return (
,
+};
+
+export default class MarkedStringDatatip extends React.PureComponent {
+ props: Props;
+
+ render(): React.Element
{
+ const elements = this.props.markedStrings.map((chunk, i) => {
+ if (chunk.type === 'markdown') {
+ return (
+
+ );
+ } else {
+ return ;
+ }
+ });
+
+ return (
+
+ {elements}
+
+ );
+ }
+}
diff --git a/pkg/nuclide-type-hint/lib/TypeHintComponent.js b/pkg/nuclide-datatip/lib/MarkedStringSnippet.js
similarity index 58%
rename from pkg/nuclide-type-hint/lib/TypeHintComponent.js
rename to pkg/nuclide-datatip/lib/MarkedStringSnippet.js
index 32b1e1a2a4..b0bc59e277 100644
--- a/pkg/nuclide-type-hint/lib/TypeHintComponent.js
+++ b/pkg/nuclide-datatip/lib/MarkedStringSnippet.js
@@ -15,49 +15,30 @@ import {AtomTextEditor} from '../../nuclide-ui/AtomTextEditor';
// Complex types can end up being super long. Truncate them.
const MAX_LENGTH = 100;
-type TypeHintComponentProps = {
- content: string,
- grammar: atom$Grammar,
-};
-
-type TypeHintComponentState = {
- isExpanded: boolean,
-};
-
-export function makeTypeHintComponent(
- content: string,
- grammar: atom$Grammar,
-): ReactClass {
- return () => ;
-}
-
-class TypeHintComponent extends React.Component {
- props: TypeHintComponentProps;
- state: TypeHintComponentState;
-
- constructor(props: TypeHintComponentProps) {
- super(props);
- this.state = {
- isExpanded: false,
- };
- }
+export default class MarkedStringSnippet extends React.Component {
+ props: {
+ value: string,
+ grammar: atom$Grammar,
+ };
+ state = {
+ isExpanded: false,
+ };
render(): React.Element {
- const value = this.props.content;
+ const {value, grammar} = this.props;
const shouldTruncate = value.length > MAX_LENGTH && !this.state.isExpanded;
const buffer = new TextBuffer(
shouldTruncate ? value.substr(0, MAX_LENGTH) + '...' : value,
);
- const {grammar} = this.props;
return (
{
this.setState({isExpanded: !this.state.isExpanded});
e.stopPropagation();
}}>
,
- range: atom$Range,
- pinnable?: boolean,
-};
+// Borrowed from the LSP API.
+export type MarkedString =
+ | {
+ type: 'markdown',
+ value: string,
+ }
+ | {
+ type: 'snippet',
+ grammar: atom$Grammar,
+ value: string,
+ };
+
+export type Datatip =
+ | {|
+ component: ReactClass,
+ range: atom$Range,
+ pinnable?: boolean,
+ |}
+ | {|
+ markedStrings: Array,
+ range: atom$Range,
+ pinnable?: boolean,
+ |};
export type PinnedDatatip = {
dispose(): void,
diff --git a/pkg/nuclide-datatip/styles/nuclide-datatip-marked.less b/pkg/nuclide-datatip/styles/nuclide-datatip-marked.less
new file mode 100644
index 0000000000..e0ee99d746
--- /dev/null
+++ b/pkg/nuclide-datatip/styles/nuclide-datatip-marked.less
@@ -0,0 +1,44 @@
+@import "ui-variables";
+@import "syntax-variables";
+
+.nuclide-datatip-marked > div:not(:last-child) {
+ border-bottom: 1px solid fade(@text-color-highlight, 10%);
+}
+
+.nuclide-datatip-marked-container {
+ color: @text-color;
+ font-family: @font-family;
+ padding: 8px;
+
+ // Avoid excess internal padding from markdown blocks.
+ :first-child {
+ margin-top: 0;
+ }
+
+ :last-child {
+ margin-bottom: 0;
+ }
+}
+
+.nuclide-datatip-marked-text-editor {
+ max-height: 300px;
+ overflow-y: auto;
+}
+
+.nuclide-datatip-marked-text-editor atom-text-editor {
+ background-color: transparent;
+ min-width: 1em; // Hack to force the AtomTextEditor to properly size itself
+ padding: 2px 0 2px 4px;
+
+ .editor-contents--private {
+ cursor: inherit!important; // Let the enclosing datatip override the cursor.
+ }
+
+ // Prevent forced scroll bar in Atom 1.9.x
+ .scroll-view .horizontal-scrollbar {
+ display: none;
+ }
+ .scrollbar-corner {
+ display: none;
+ }
+}
diff --git a/pkg/nuclide-type-hint/lib/TypeHintManager.js b/pkg/nuclide-type-hint/lib/TypeHintManager.js
index a973e03e74..e4fd8ce776 100644
--- a/pkg/nuclide-type-hint/lib/TypeHintManager.js
+++ b/pkg/nuclide-type-hint/lib/TypeHintManager.js
@@ -13,7 +13,6 @@ import type {Datatip} from '../../nuclide-datatip/lib/types';
import {arrayRemove} from '../../commons-node/collection';
import {track, trackTiming} from '../../nuclide-analytics';
-import {makeTypeHintComponent} from './TypeHintComponent';
import {getLogger} from '../../nuclide-logging';
const logger = getLogger();
@@ -59,7 +58,7 @@ export default class TypeHintManager {
message: hint,
});
return {
- component: makeTypeHintComponent(hint, grammar),
+ markedStrings: [{type: 'snippet', value: hint, grammar}],
range,
};
}
diff --git a/pkg/nuclide-type-hint/styles/nuclide-type-hint.less b/pkg/nuclide-type-hint/styles/nuclide-type-hint.less
deleted file mode 100644
index 866ac2a98e..0000000000
--- a/pkg/nuclide-type-hint/styles/nuclide-type-hint.less
+++ /dev/null
@@ -1,49 +0,0 @@
-@import "ui-variables";
-
-.nuclide-type-hint-overlay {
- background: @app-background-color;
- box-shadow: 0px 1px 4px 0px rgba(0,0,0,1);
- color: rgb(103, 103, 103);
- font-family: Menlo, Monaco, Consolas, monospace;
- font-size: @font-size;
- padding: 4px 10px;
- position: relative;
- white-space: nowrap;
-}
-
-.nuclide-type-hint-expandable-chevron {
- cursor: pointer;
-}
-
-.nuclide-type-hint-text-editor-container {
- display: flex;
- flex-grow: 1;
- max-width: 60em;
-}
-
-.nuclide-type-hint-text-editor {
- max-height: 300px;
- overflow-y: auto;
-}
-
-.nuclide-type-hint-text-editor atom-text-editor {
- background-color: transparent;
- min-width: 1em; // Hack to force the AtomTextEditor to properly size itself
- padding: 2px 0 2px 4px;
-
- .editor-contents--private {
- cursor: inherit!important; // Let the enclosing datatip override the cursor.
- }
-
- // Prevent forced scroll bar in Atom 1.9.x
- .scroll-view .horizontal-scrollbar {
- display: none;
- }
- .scrollbar-corner {
- display: none;
- }
-}
-
-atom-text-editor .nuclide-type-hint-highlight-region > .region {
- background: @background-color-highlight;
-}
diff --git a/pkg/sample-datatip/lib/SampleDatatip.js b/pkg/sample-datatip/lib/SampleDatatip.js
index cc6badee18..76e2e12234 100644
--- a/pkg/sample-datatip/lib/SampleDatatip.js
+++ b/pkg/sample-datatip/lib/SampleDatatip.js
@@ -15,7 +15,10 @@ import {makeSampleDatatipComponent} from './SampleDatatipComponent';
const WORD_REGEX = /\w+/gi;
-export async function datatip(editor: TextEditor, position: atom$Point): Promise {
+export async function datatip(
+ editor: TextEditor,
+ position: atom$Point,
+): Promise {
const extractedWord = wordAtPosition(editor, position, WORD_REGEX);
if (extractedWord == null) {
return null;
@@ -25,7 +28,40 @@ export async function datatip(editor: TextEditor, position: atom$Point): Promise
range,
} = extractedWord;
const word = wordMatch[0] == null ? 'N/A' : wordMatch[0];
+ if (editor.getGrammar().scopeName === 'source.gfm') {
+ // Demo of the Markdown string-based API.
+ return {
+ markedStrings: [
+ {
+ type: 'markdown',
+ value: `An h1 header
+============
+
+Paragraphs are separated by a blank line.
+
+2nd paragraph. *Italic*, **bold**, and \`monospace\`. Itemized lists
+look like:
+
+ * this one
+ * that one
+ * the other one`,
+ },
+ {
+ type: 'snippet',
+ grammar: atom.grammars.selectGrammar('js', ''),
+ value: 'function f(x: number): boolean {',
+ },
+ {
+ type: 'snippet',
+ grammar: atom.grammars.selectGrammar('js', ''),
+ value: 'function f: (number) => boolean',
+ },
+ ],
+ range,
+ };
+ }
return {
+ // For more complex use cases, provide a custom React component.
component: makeSampleDatatipComponent(word),
range,
};