Skip to content

Commit

Permalink
Merge pull request #497 from microsoft/wip_workbench
Browse files Browse the repository at this point in the history
Make the playground run in other pages, and start of a workbench page
  • Loading branch information
Orta authored Apr 13, 2020
2 parents 7e623a3 + 13a7853 commit 0e21711
Show file tree
Hide file tree
Showing 44 changed files with 1,927 additions and 1,098 deletions.
3 changes: 2 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"semi": false,
"singleQuote": false,
"printWidth": 120
"printWidth": 120,
"arrowParens": "avoid"
}
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@
"clean": "yarn workspace typescriptlang-org gatsby clean",
"test": "CI=true yarn workspaces run test"
},
"prettier": {
"printWidth": 120,
"semi": false,
"singleQuote": true,
"trailingComma": "es5"
},
"dependencies": {
"serve-handler": "^6.1.2"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.0.1",
"main": "dist/index.js",
"license": "MIT",
"keywords":["playground-plugin"],
"scripts": {
"build": "rollup -c rollup.config.js;",
"compile": "tsc",
Expand Down
60 changes: 30 additions & 30 deletions packages/gatsby-remark-shiki-twoslash/src/renderer.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// This started as a JS port of https://github.com/octref/shiki/blob/master/packages/shiki/src/renderer.ts

type Lines = import('shiki').IThemedToken[][]
type Options = import('shiki/dist/renderer').HtmlRendererOptions
type TwoSlash = import('@typescript/twoslash').TwoSlashReturn
type Lines = import("shiki").IThemedToken[][]
type Options = import("shiki/dist/renderer").HtmlRendererOptions
type TwoSlash = import("@typescript/twoslash").TwoSlashReturn

import { stripHTML, createHighlightedString2 } from './utils'
import { stripHTML, createHighlightedString2 } from "./utils"

// OK, so - this is just straight up complex code.

Expand All @@ -30,18 +30,18 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash
return plainOleShikiRenderer(lines, options)
}

let html = ''
let html = ""

html += `<pre class="shiki twoslash">`
if (options.langId) {
html += `<div class="language-id">${options.langId}</div>`
}
html += `<div class='code-container'><code>`

const errorsGroupedByLine = groupBy(twoslash.errors, (e) => e.line) || new Map()
const staticQuickInfosGroupedByLine = groupBy(twoslash.staticQuickInfos, (q) => q.line) || new Map()
const errorsGroupedByLine = groupBy(twoslash.errors, e => e.line) || new Map()
const staticQuickInfosGroupedByLine = groupBy(twoslash.staticQuickInfos, q => q.line) || new Map()
// A query is always about the line above it!
const queriesGroupedByLine = groupBy(twoslash.queries, (q) => q.line - 1) || new Map()
const queriesGroupedByLine = groupBy(twoslash.queries, q => q.line - 1) || new Map()

let filePos = 0
lines.forEach((l, i) => {
Expand All @@ -60,8 +60,8 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash
// errors and lang serv identifiers
let tokenPos = 0

l.forEach((token) => {
let tokenContent = ''
l.forEach(token => {
let tokenContent = ""
// Underlining particular words
const findTokenFunc = (start: number) => (e: any) =>
start <= e.character && start + token.content.length >= e.character + e.length
Expand All @@ -71,8 +71,8 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash
// prettier-ignore
console.log(result, start, '<=', e.character, '&&', start + token.content.length, '<=', e.character + e.length)
if (result) {
console.log('Found:', e)
console.log('Inside:', token)
console.log("Found:", e)
console.log("Inside:", token)
}
return result
}
Expand All @@ -87,7 +87,7 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash
})

if (allTokensByStart.length) {
const ranges = allTokensByStart.map((token) => {
const ranges = allTokensByStart.map(token => {
const range: any = {
begin: token.start! - filePos,
end: token.start! + token.length! - filePos,
Expand All @@ -101,11 +101,11 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash
// throw new Error(`The begin range of a token is at a minus location, filePos:${filePos} current token: ${JSON.stringify(token, null, ' ')}\n result: ${JSON.stringify(range, null, ' ')}`)
}

if ('renderedMessage' in token) range.classes = 'err'
if ('kind' in token) range.classes = token.kind
if ('targetString' in token) {
range.classes = 'lsp'
range['lsp'] = stripHTML(token.text)
if ("renderedMessage" in token) range.classes = "err"
if ("kind" in token) range.classes = token.kind
if ("targetString" in token) {
range.classes = "lsp"
range["lsp"] = stripHTML(token.text)
}
return range
})
Expand All @@ -126,34 +126,34 @@ export function renderToHTML(lines: Lines, options: Options, twoslash?: TwoSlash

// Adding error messages to the line after
if (errors.length) {
const messages = errors.map((e) => escapeHtml(e.renderedMessage)).join('</br>')
const codes = errors.map((e) => e.code).join('<br/>')
const messages = errors.map(e => escapeHtml(e.renderedMessage)).join("</br>")
const codes = errors.map(e => e.code).join("<br/>")
html += `<span class="error"><span>${messages}</span><span class="code">${codes}</span></span>`
html += `<span class="error-behind">${messages}</span>`
}

// Add queries to the next line
if (queries.length) {
queries.forEach((query) => {
html += `<span class='query'>${'//' + ''.padStart(query.offset - 2) + '^ = ' + query.text}</span>`
queries.forEach(query => {
html += `<span class='query'>${"//" + "".padStart(query.offset - 2) + "^ = " + query.text}</span>`
})
html += '\n'
html += "\n"
}
})
html = html.replace(/\n*$/, '') // Get rid of final new lines
html = html.replace(/\n*$/, "") // Get rid of final new lines
html += `</code></div></pre>`

return html
}

function escapeHtml(html: string) {
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;')
return html.replace(/</g, "&lt;").replace(/>/g, "&gt;")
}

/** Returns a map where all the keys are the value in keyGetter */
function groupBy<T>(list: T[], keyGetter: (obj: any) => number) {
const map = new Map<number, T[]>()
list.forEach((item) => {
list.forEach(item => {
const key = keyGetter(item)
const collection = map.get(key)
if (!collection) {
Expand All @@ -166,7 +166,7 @@ function groupBy<T>(list: T[], keyGetter: (obj: any) => number) {
}

export function plainOleShikiRenderer(lines: Lines, options: Options) {
let html = ''
let html = ""

html += `<pre class="shiki">`
if (options.langId) {
Expand All @@ -175,18 +175,18 @@ export function plainOleShikiRenderer(lines: Lines, options: Options) {

html += `<div class='code-container'><code>`

lines.forEach((l) => {
lines.forEach(l => {
if (l.length === 0) {
html += `\n`
} else {
l.forEach((token) => {
l.forEach(token => {
html += `<span style="color: ${token.color}">${escapeHtml(token.content)}</span>`
})
html += `\n`
}
})

html = html.replace(/\n*$/, '') // Get rid of final new lines
html = html.replace(/\n*$/, "") // Get rid of final new lines
html += `</code></div></pre>`
return html
}
3 changes: 2 additions & 1 deletion packages/playground-examples/.prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
"singleQuote": false,
"tabWidth": 2,
"printWidth": 120,
"trailingComma": "es5"
"trailingComma": "es5",
"arrowParens": "avoid"
}
88 changes: 44 additions & 44 deletions packages/playground/src/createElements.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { PlaygroundPlugin } from '.'
import { PlaygroundPlugin } from "."

type Sandbox = import('typescript-sandbox').Sandbox
type Sandbox = import("typescript-sandbox").Sandbox

export const createDragBar = () => {
const sidebar = document.createElement('div')
sidebar.className = 'playground-dragbar'
const sidebar = document.createElement("div")
sidebar.className = "playground-dragbar"

let left: HTMLElement, right: HTMLElement
const drag = (e: MouseEvent) => {
Expand All @@ -23,8 +23,8 @@ export const createDragBar = () => {

// Save the x coordinate of the
if (window.localStorage) {
window.localStorage.setItem('dragbar-x', '' + clampedOffset)
window.localStorage.setItem('dragbar-window-width', '' + window.innerWidth)
window.localStorage.setItem("dragbar-x", "" + clampedOffset)
window.localStorage.setItem("dragbar-window-width", "" + window.innerWidth)
}

// @ts-ignore - I know what I'm doing
Expand All @@ -36,45 +36,45 @@ export const createDragBar = () => {
}
}

sidebar.addEventListener('mousedown', e => {
left = document.getElementById('editor-container')!
right = sidebar.parentElement?.getElementsByClassName('playground-sidebar').item(0)! as any
sidebar.addEventListener("mousedown", e => {
left = document.getElementById("editor-container")!
right = sidebar.parentElement?.getElementsByClassName("playground-sidebar").item(0)! as any
// Handle dragging all over the screen
document.addEventListener('mousemove', drag)
document.addEventListener("mousemove", drag)
// Remove it when you lt go anywhere
document.addEventListener('mouseup', () => {
document.removeEventListener('mousemove', drag)
document.body.style.userSelect = 'auto'
document.addEventListener("mouseup", () => {
document.removeEventListener("mousemove", drag)
document.body.style.userSelect = "auto"
})

// Don't allow the drag to select text accidentally
document.body.style.userSelect = 'none'
document.body.style.userSelect = "none"
e.stopPropagation()
e.cancelBubble = true
})

return sidebar
}

export const sidebarHidden = () => !!window.localStorage.getItem('sidebar-hidden')
export const sidebarHidden = () => !!window.localStorage.getItem("sidebar-hidden")

export const createSidebar = () => {
const sidebar = document.createElement('div')
sidebar.className = 'playground-sidebar'
const sidebar = document.createElement("div")
sidebar.className = "playground-sidebar"

// Start with the sidebar hidden on small screens
const isTinyScreen = window.innerWidth < 800

// This is independent of the sizing below so that you keep the same sized sidebar
if (isTinyScreen || sidebarHidden()) {
sidebar.style.display = 'none'
sidebar.style.display = "none"
}

if (window.localStorage && window.localStorage.getItem('dragbar-x')) {
if (window.localStorage && window.localStorage.getItem("dragbar-x")) {
// Don't restore the x pos if the window isn't the same size
if (window.innerWidth === Number(window.localStorage.getItem('dragbar-window-width'))) {
if (window.innerWidth === Number(window.localStorage.getItem("dragbar-window-width"))) {
// Set the dragger to the previous x pos
let width = window.localStorage.getItem('dragbar-x')
let width = window.localStorage.getItem("dragbar-x")

if (isTinyScreen) {
width = String(Math.min(Number(width), 280))
Expand All @@ -84,38 +84,38 @@ export const createSidebar = () => {
sidebar.style.flexBasis = `${width}px`
sidebar.style.maxWidth = `${width}px`

const left = document.getElementById('editor-container')!
const left = document.getElementById("editor-container")!
left.style.width = `calc(100% - ${width}px)`
}
}

return sidebar
}

const toggleIconWhenOpen = '&#x21E5;'
const toggleIconWhenClosed = '&#x21E4;'
const toggleIconWhenOpen = "&#x21E5;"
const toggleIconWhenClosed = "&#x21E4;"

export const setupSidebarToggle = () => {
const toggle = document.getElementById('sidebar-toggle')!
const toggle = document.getElementById("sidebar-toggle")!

const updateToggle = () => {
const sidebar = window.document.querySelector('.playground-sidebar') as HTMLDivElement
const sidebarShowing = sidebar.style.display !== 'none'
const sidebar = window.document.querySelector(".playground-sidebar") as HTMLDivElement
const sidebarShowing = sidebar.style.display !== "none"

toggle.innerHTML = sidebarShowing ? toggleIconWhenOpen : toggleIconWhenClosed
toggle.setAttribute('aria-label', sidebarShowing ? 'Hide Sidebar' : 'Show Sidebar')
toggle.setAttribute("aria-label", sidebarShowing ? "Hide Sidebar" : "Show Sidebar")
}

toggle.onclick = () => {
const sidebar = window.document.querySelector('.playground-sidebar') as HTMLDivElement
const newState = sidebar.style.display !== 'none'
const sidebar = window.document.querySelector(".playground-sidebar") as HTMLDivElement
const newState = sidebar.style.display !== "none"

if (newState) {
localStorage.setItem('sidebar-hidden', 'true')
sidebar.style.display = 'none'
localStorage.setItem("sidebar-hidden", "true")
sidebar.style.display = "none"
} else {
localStorage.removeItem('sidebar-hidden')
sidebar.style.display = 'block'
localStorage.removeItem("sidebar-hidden")
sidebar.style.display = "block"
}

updateToggle()
Expand All @@ -131,19 +131,19 @@ export const setupSidebarToggle = () => {
}

export const createTabBar = () => {
const tabBar = document.createElement('div')
tabBar.classList.add('playground-plugin-tabview')
const tabBar = document.createElement("div")
tabBar.classList.add("playground-plugin-tabview")
return tabBar
}

export const createPluginContainer = () => {
const container = document.createElement('div')
container.classList.add('playground-plugin-container')
const container = document.createElement("div")
container.classList.add("playground-plugin-container")
return container
}

export const createTabForPlugin = (plugin: PlaygroundPlugin) => {
const element = document.createElement('button')
const element = document.createElement("button")
element.textContent = plugin.displayName
return element
}
Expand All @@ -163,13 +163,13 @@ export const activatePlugin = (
}

// @ts-ignore
if (!newPluginTab) throw new Error('Could not get a tab for the plugin: ' + plugin.displayName)
if (!newPluginTab) throw new Error("Could not get a tab for the plugin: " + plugin.displayName)

// Tell the old plugin it's getting the boot
// @ts-ignore
if (previousPlugin && oldPluginTab) {
if (previousPlugin.willUnmount) previousPlugin.willUnmount(sandbox, container)
oldPluginTab.classList.remove('active')
oldPluginTab.classList.remove("active")
}

// Wipe the sidebar
Expand All @@ -178,12 +178,12 @@ export const activatePlugin = (
}

// Start booting up the new plugin
newPluginTab.classList.add('active')
newPluginTab.classList.add("active")

// Tell the new plugin to start doing some work
if (plugin.willMount) plugin.willMount(sandbox, container)
if (plugin.modelChanged) plugin.modelChanged(sandbox, sandbox.getModel())
if (plugin.modelChangedDebounce) plugin.modelChangedDebounce(sandbox, sandbox.getModel())
if (plugin.modelChanged) plugin.modelChanged(sandbox, sandbox.getModel(), container)
if (plugin.modelChangedDebounce) plugin.modelChangedDebounce(sandbox, sandbox.getModel(), container)
if (plugin.didMount) plugin.didMount(sandbox, container)

// Let the previous plugin do any slow work after it's all done
Expand Down
Loading

0 comments on commit 0e21711

Please sign in to comment.