mirror of https://github.com/onivim/oni.git
Markdown Syntax Highlights (#2332)
* Basic code highlights. * Check if the given language is valid, use auto highlight if not. * Fix check. * Set baseUrl as well. Unrelated to code blocks, but fixes #1653 and is a single line change in the options I've added. * Bump marked types package number. * Move style sheet to only markdown preview. * Swap theme to option. * Fix review comment from previous PR. * Fix nohighlights. * Update tests for Markdown preview. * Fix lint error. * Add config option for syntax highlights.
This commit is contained in:
parent
759918d17b
commit
b901ecdda0
|
@ -4,7 +4,7 @@
|
|||
* Entry point for browser integration plugin
|
||||
*/
|
||||
|
||||
import { ipcRenderer, shell, WebviewTag } from "electron"
|
||||
import { shell, WebviewTag } from "electron"
|
||||
import * as React from "react"
|
||||
|
||||
import * as Oni from "oni-api"
|
||||
|
@ -290,10 +290,6 @@ export const activate = (
|
|||
detail: "",
|
||||
enabled: isBrowserScrollCommandEnabled,
|
||||
})
|
||||
|
||||
ipcRenderer.on("open-oni-browser", (event: string, args: string) => {
|
||||
openUrl(args)
|
||||
})
|
||||
}
|
||||
|
||||
export const registerAchievements = (achievements: AchievementsManager) => {
|
||||
|
|
|
@ -81,6 +81,8 @@ const BaseConfiguration: IConfigurationValues = {
|
|||
"experimental.indentLines.bannedFiletypes": [],
|
||||
"experimental.markdownPreview.enabled": false,
|
||||
"experimental.markdownPreview.autoScroll": true,
|
||||
"experimental.markdownPreview.syntaxHighlights": true,
|
||||
"experimental.markdownPreview.syntaxTheme": "atom-one-dark",
|
||||
|
||||
"experimental.neovim.transport": "stdio",
|
||||
// TODO: Enable pipe transport for Windows
|
||||
|
|
|
@ -65,6 +65,8 @@ export interface IConfigurationValues {
|
|||
// Whether the markdown preview pane should be shown
|
||||
"experimental.markdownPreview.enabled": boolean
|
||||
"experimental.markdownPreview.autoScroll": boolean
|
||||
"experimental.markdownPreview.syntaxHighlights": boolean
|
||||
"experimental.markdownPreview.syntaxTheme": string
|
||||
|
||||
// The transport to use for Neovim
|
||||
// Valid values are "stdio" and "pipe"
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"tbd-dependencies": {
|
||||
"marked": "^0.4.0",
|
||||
"dompurify": "1.0.2",
|
||||
"highlight.js": "^9.12.0",
|
||||
"oni-types": "^0.0.4",
|
||||
"oni-api": "^0.0.9"
|
||||
},
|
||||
|
@ -31,6 +32,7 @@
|
|||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"tbd-devDependencies": {
|
||||
"@types/highlight.js": "^9.12.2",
|
||||
"typescript": "2.5.3",
|
||||
"rimraf": "2.6.2"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { EventCallback, IDisposable, IEvent } from "oni-types"
|
||||
|
||||
import * as dompurify from "dompurify"
|
||||
import * as hljs from "highlight.js"
|
||||
import * as marked from "marked"
|
||||
import * as Oni from "oni-api"
|
||||
import * as React from "react"
|
||||
|
@ -84,6 +85,9 @@ class MarkdownPreview extends React.PureComponent<IMarkdownPreviewProps, IMarkdo
|
|||
|
||||
private generateContainerStyle(): string {
|
||||
const colors = this.state.colors
|
||||
const syntaxHighlightTheme = this.props.oni.configuration.getValue(
|
||||
"experimental.markdownPreview.syntaxTheme",
|
||||
)
|
||||
|
||||
const codeBlockStyle = `
|
||||
background: ${colors.codeBackground};
|
||||
|
@ -95,6 +99,8 @@ class MarkdownPreview extends React.PureComponent<IMarkdownPreviewProps, IMarkdo
|
|||
`
|
||||
|
||||
return `
|
||||
<link rel="stylesheet" href="node_modules/highlight.js/styles/${syntaxHighlightTheme}.css">
|
||||
|
||||
<style>
|
||||
.oniPluginMarkdownPreviewContainerStyle {
|
||||
padding: 1em 1em 1em 1em;
|
||||
|
@ -143,6 +149,38 @@ class MarkdownPreview extends React.PureComponent<IMarkdownPreviewProps, IMarkdo
|
|||
markdownLines.splice(0, 0, generateAnchor(i))
|
||||
markdownLines.push(generateAnchor(originalLinesCount - 1))
|
||||
|
||||
const markedOptions = {
|
||||
baseUrl: this.props.oni.workspace.activeWorkspace,
|
||||
highlight(code, lang) {
|
||||
return code
|
||||
},
|
||||
}
|
||||
|
||||
const highlightsEnabled = this.props.oni.configuration.getValue(
|
||||
"experimental.markdownPreview.syntaxHighlights",
|
||||
)
|
||||
|
||||
if (highlightsEnabled) {
|
||||
markedOptions.highlight = (code, lang) => {
|
||||
const languageExists = hljs.getLanguage(lang)
|
||||
const languageNotDefinedOrInvalid =
|
||||
typeof lang === "undefined" ||
|
||||
(typeof languageExists === "undefined" && lang !== "nohighlight")
|
||||
|
||||
if (languageNotDefinedOrInvalid) {
|
||||
return hljs.highlightAuto(code).value
|
||||
}
|
||||
|
||||
if (lang === "nohighlight") {
|
||||
return code
|
||||
}
|
||||
|
||||
return hljs.highlight(lang, code).value
|
||||
}
|
||||
}
|
||||
|
||||
marked.setOptions(markedOptions)
|
||||
|
||||
return marked(markdownLines.join("\n"))
|
||||
}
|
||||
|
||||
|
|
|
@ -258,7 +258,7 @@ export function createWindow(
|
|||
if (!isDevelopment) {
|
||||
currentWindow.webContents.on("will-navigate", (event, url) => {
|
||||
event.preventDefault()
|
||||
currentWindow.webContents.send("open-oni-browser", url)
|
||||
currentWindow.webContents.send("execute-command", "browser.openUrl", url)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -699,6 +699,7 @@
|
|||
"electron-settings": "^3.1.4",
|
||||
"find-up": "2.1.0",
|
||||
"fs-extra": "^5.0.0",
|
||||
"highlight.js": "^9.12.0",
|
||||
"json5": "^1.0.1",
|
||||
"keyboard-layout": "^2.0.13",
|
||||
"marked": "^0.4.0",
|
||||
|
@ -734,12 +735,13 @@
|
|||
"@types/electron-settings": "^3.1.1",
|
||||
"@types/enzyme": "^3.1.8",
|
||||
"@types/fs-extra": "^5.0.2",
|
||||
"@types/highlight.js": "^9.12.2",
|
||||
"@types/jest": "^22.1.3",
|
||||
"@types/jsdom": "11.0.0",
|
||||
"@types/json5": "^0.0.29",
|
||||
"@types/lodash": "4.14.38",
|
||||
"@types/lolex": "2.1.0",
|
||||
"@types/marked": "^0.3.0",
|
||||
"@types/marked": "^0.4.0",
|
||||
"@types/minimatch": "3.0.1",
|
||||
"@types/minimist": "1.1.29",
|
||||
"@types/mkdirp": "0.3.29",
|
||||
|
|
|
@ -3,7 +3,13 @@
|
|||
*/
|
||||
|
||||
import { Assertor } from "./Assert"
|
||||
import { awaitEditorMode, getTemporaryFilePath, insertText, navigateToFile } from "./Common"
|
||||
import {
|
||||
awaitEditorMode,
|
||||
getElementByClassName,
|
||||
getTemporaryFilePath,
|
||||
insertText,
|
||||
navigateToFile,
|
||||
} from "./Common"
|
||||
|
||||
import * as Oni from "oni-api"
|
||||
|
||||
|
@ -11,6 +17,7 @@ interface IMarkdownPreviewPlugin {
|
|||
isPaneOpen(): boolean
|
||||
getUnrenderedContent(): string
|
||||
getRenderedContent(): string
|
||||
toggle(): void
|
||||
}
|
||||
|
||||
export const settings = {
|
||||
|
@ -24,25 +31,91 @@ export async function test(oni: Oni.Plugin.Api) {
|
|||
|
||||
await oni.automation.waitForEditors()
|
||||
|
||||
// Wait for Plugin to be loaded
|
||||
await oni.automation.waitFor(() => oni.plugins.loaded)
|
||||
const markdownPlugin = oni.plugins.getPlugin(
|
||||
"oni-plugin-markdown-preview",
|
||||
) as IMarkdownPreviewPlugin
|
||||
assert.defined(markdownPlugin, "plugin instance")
|
||||
|
||||
assert.assert(!markdownPlugin.isPaneOpen(), "Preview pane is not initially closed")
|
||||
// Check its not open by default
|
||||
assert.assert(!markdownPlugin.isPaneOpen(), "Preview pane is initially closed.")
|
||||
|
||||
await navigateToFile(getTemporaryFilePath("md"), oni)
|
||||
// Check it opens when navigating into a markdown file.
|
||||
const markdownFilePath = getTemporaryFilePath("md")
|
||||
await navigateToFile(markdownFilePath, oni)
|
||||
await oni.automation.waitFor(() => markdownPlugin.isPaneOpen())
|
||||
|
||||
assert.isEmpty(
|
||||
markdownPlugin.getUnrenderedContent().trim(),
|
||||
"Preview pane for empty Markdown buffer",
|
||||
"Preview pane shown for Markdown buffer.",
|
||||
)
|
||||
|
||||
// Check a Title is rendered correctly.
|
||||
await insertText(oni, "# Title 1")
|
||||
assert.contains(
|
||||
markdownPlugin.getRenderedContent(),
|
||||
">Title 1</h1>",
|
||||
"Preview pane with rendered header element",
|
||||
"Preview pane with renders header element.",
|
||||
)
|
||||
|
||||
// Check syntax highlights are applied.
|
||||
oni.automation.sendKeys("o")
|
||||
await oni.automation.waitFor(() => oni.editors.activeEditor.mode === "insert")
|
||||
|
||||
oni.automation.sendKeys("```<enter>")
|
||||
oni.automation.sendKeys("const test = 'Oni'<enter>")
|
||||
oni.automation.sendKeys("```")
|
||||
oni.automation.sendKeys("<esc>")
|
||||
|
||||
await oni.automation.waitFor(() => oni.editors.activeEditor.mode === "normal")
|
||||
|
||||
assert.contains(
|
||||
markdownPlugin.getRenderedContent(),
|
||||
"code>",
|
||||
"Code block present in preview HTML.",
|
||||
)
|
||||
|
||||
assert.assert(
|
||||
getElementByClassName("hljs-keyword").innerText === "const",
|
||||
"Syntax highlights present in preview HTML.",
|
||||
)
|
||||
|
||||
// Finally, test swapping between files.
|
||||
// Swapping to a non-MD file should close it.
|
||||
// Swapping to an MD file should open it.
|
||||
// Closing the MD preview manually should cause it to remain closed.
|
||||
|
||||
const nonMDFilePath = getTemporaryFilePath("ts")
|
||||
|
||||
await navigateToFile(nonMDFilePath, oni)
|
||||
await oni.automation.waitFor(
|
||||
() => oni.editors.activeEditor.activeBuffer.language === "typescript",
|
||||
)
|
||||
assert.assert(!markdownPlugin.isPaneOpen(), "Preview pane is closed for non-MD file.")
|
||||
|
||||
await navigateToFile(markdownFilePath, oni)
|
||||
await oni.automation.waitFor(
|
||||
() => oni.editors.activeEditor.activeBuffer.language === "markdown",
|
||||
)
|
||||
assert.assert(markdownPlugin.isPaneOpen(), "Preview pane opens for MD file.")
|
||||
|
||||
// Manually closing preview, should remain closed now.
|
||||
markdownPlugin.toggle()
|
||||
assert.assert(!markdownPlugin.isPaneOpen(), "Preview pane closes on toggle.")
|
||||
|
||||
await navigateToFile(nonMDFilePath, oni)
|
||||
await oni.automation.waitFor(
|
||||
() => oni.editors.activeEditor.activeBuffer.language === "typescript",
|
||||
)
|
||||
await navigateToFile(markdownFilePath, oni)
|
||||
await oni.automation.waitFor(
|
||||
() => oni.editors.activeEditor.activeBuffer.language === "markdown",
|
||||
)
|
||||
|
||||
// Should remain closed when swapping back in.
|
||||
assert.assert(
|
||||
!markdownPlugin.isPaneOpen(),
|
||||
"Preview pane remains closed for MD file when manually closed.",
|
||||
)
|
||||
}
|
||||
|
|
14
yarn.lock
14
yarn.lock
|
@ -110,6 +110,10 @@
|
|||
"@types/minimatch" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/highlight.js@^9.12.2":
|
||||
version "9.12.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.3.tgz#b672cfaac25cbbc634a0fd92c515f66faa18dbca"
|
||||
|
||||
"@types/jest@^22.1.3":
|
||||
version "22.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.1.3.tgz#25da391935e6fac537551456f077ce03144ec168"
|
||||
|
@ -134,9 +138,9 @@
|
|||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/lolex/-/lolex-2.1.0.tgz#7a53611a6c970574173a7d92ce9a0da000652725"
|
||||
|
||||
"@types/marked@^0.3.0":
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.3.0.tgz#583c223dd33385a1dda01aaf77b0cd0411c4b524"
|
||||
"@types/marked@^0.4.0":
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.4.0.tgz#057a6165703e7419217f8ffc6887747f980b6315"
|
||||
|
||||
"@types/minimatch@*":
|
||||
version "3.0.3"
|
||||
|
@ -4855,6 +4859,10 @@ he@1.1.1, he@1.1.x:
|
|||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
|
||||
|
||||
highlight.js@^9.12.0:
|
||||
version "9.12.0"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.12.0.tgz#e6d9dbe57cbefe60751f02af336195870c90c01e"
|
||||
|
||||
hmac-drbg@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
|
||||
|
|
Loading…
Reference in New Issue