Skip to content

Commit

Permalink
🥒 highlightjs tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
fonsp committed Dec 20, 2021
1 parent 1b229b8 commit d851168
Show file tree
Hide file tree
Showing 4 changed files with 294 additions and 4 deletions.
11 changes: 8 additions & 3 deletions frontend/components/CellOutput.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ import { EditorState, EditorView, julia_andrey, defaultHighlightStyle } from "..
import { pluto_syntax_colors } from "./CellInput.js"
import { useState } from "../imports/Preact.js"

// @ts-ignore
import hljs from "https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/es/highlight.min.js"
import hljs from "../imports/highlightjs.js"

export class CellOutput extends Component {
constructor() {
Expand Down Expand Up @@ -445,7 +444,13 @@ export let highlight = (code_element, language) => {
language = language === "jl" ? "julia" : language

if (code_element.children.length === 0) {
if (language === "julia") {
if (
language === "julia" &&
// CodeMirror does not want to render inside a `<details>`...
// I tried to debug this, it does not happen on a clean webpage with the same CM versions:
// https://glitch.com/edit/#!/wobbly-sweet-fibre?path=script.js%3A51%3A76
code_element.closest("details") == null
) {
const editorview = new EditorView({
state: EditorState.create({
// Remove references to `Main.workspace#xx.` in the docs since
Expand Down
13 changes: 12 additions & 1 deletion frontend/editor.css
Original file line number Diff line number Diff line change
Expand Up @@ -1892,6 +1892,7 @@ pluto-helpbox > header {
background-color: #eef1f7;
color: hsl(230, 14%, 11%);
font-family: "Roboto Mono", monospace;
font-size: 0.9rem;
font-weight: 700;
border-top-left-radius: 6px;
border-top-right-radius: 6px;
Expand Down Expand Up @@ -1957,18 +1958,28 @@ pluto-helpbox.hidden > section {
pluto-helpbox {
font-family: "Lato", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", "Helvetica", "Arial", sans-serif;
line-height: 1.5;
font-size: 0.9rem;
}
pluto-helpbox pre,
pluto-helpbox code,
pluto-helpbox .cm-line {
/* from https://docs.julialang.org/en/v1/assets/themes/documenter-light.css */
font-family: "Roboto Mono", "SFMono-Regular", "Menlo", "Consolas", "Liberation Mono", "DejaVu Sans Mono", monospace;
font-size: 0.875em;
font-size: 0.95em;
}
pluto-helpbox pre code {
font-size: 1em;
}

pluto-helpbox pre code.hljs {
padding: 0;
background: none;
}

pluto-helpbox code .cm-editor .cm-content {
padding: 0px;
}

pluto-helpbox > section {
height: 100%;
overflow: auto;
Expand Down
261 changes: 261 additions & 0 deletions frontend/imports/highlightjs.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// from https://github.com/highlightjs/highlight.js/blob/11.3.1/types/index.d.ts with some modifications

/* eslint-disable no-unused-vars */
/* eslint-disable no-use-before-define */
// For TS consumers who use Node and don't have dom in their tsconfig lib, import the necessary types here.
/// <reference lib="dom" />


type MatchType = "begin" | "end" | "illegal"
type EnhancedMatch = RegExpMatchArray & {rule: CompiledMode, type: MatchType}
type AnnotatedError = Error & {mode?: Mode | Language, languageName?: string, badRule?: Mode}

type KeywordData = [string, number];
type KeywordDict = Record<string, KeywordData>


export type HLJSApi = PublicApi & ModesAPI

export interface VuePlugin {
install: (vue: any) => void
}

// perhaps make this an interface?
type RegexEitherOptions = {
capture?: boolean
}

interface PublicApi {
highlight: (codeOrLanguageName: string, optionsOrCode: string | HighlightOptions, ignoreIllegals?: boolean) => HighlightResult
highlightAuto: (code: string, languageSubset?: string[]) => AutoHighlightResult
highlightBlock: (element: HTMLElement) => void
highlightElement: (element: HTMLElement) => void
configure: (options: Partial<HLJSOptions>) => void
initHighlighting: () => void
initHighlightingOnLoad: () => void
highlightAll: () => void
registerLanguage: (languageName: string, language: LanguageFn) => void
unregisterLanguage: (languageName: string) => void
listLanguages: () => string[]
registerAliases: (aliasList: string | string[], { languageName } : {languageName: string}) => void
getLanguage: (languageName: string) => Language | undefined
autoDetection: (languageName: string) => boolean
inherit: <T>(original: T, ...args: Record<string, any>[]) => T
addPlugin: (plugin: HLJSPlugin) => void
debugMode: () => void
safeMode: () => void
versionString: string
vuePlugin: () => VuePlugin
regex: {
concat: (...args: (RegExp | string)[]) => string,
lookahead: (re: RegExp | string) => string,
either: (...args: (RegExp | string)[] | [...(RegExp | string)[], RegexEitherOptions]) => string,
optional: (re: RegExp | string) => string,
anyNumberOfTimes: (re: RegExp | string) => string
}
}

interface ModesAPI {
SHEBANG: (mode?: Partial<Mode> & {binary?: string | RegExp}) => Mode
BACKSLASH_ESCAPE: Mode
QUOTE_STRING_MODE: Mode
APOS_STRING_MODE: Mode
PHRASAL_WORDS_MODE: Mode
COMMENT: (begin: string | RegExp, end: string | RegExp, modeOpts?: Mode | {}) => Mode
C_LINE_COMMENT_MODE: Mode
C_BLOCK_COMMENT_MODE: Mode
HASH_COMMENT_MODE: Mode
NUMBER_MODE: Mode
C_NUMBER_MODE: Mode
BINARY_NUMBER_MODE: Mode
REGEXP_MODE: Mode
TITLE_MODE: Mode
UNDERSCORE_TITLE_MODE: Mode
METHOD_GUARD: Mode
END_SAME_AS_BEGIN: (mode: Mode) => Mode
// built in regex
IDENT_RE: string
UNDERSCORE_IDENT_RE: string
MATCH_NOTHING_RE: string
NUMBER_RE: string
C_NUMBER_RE: string
BINARY_NUMBER_RE: string
RE_STARTERS_RE: string
}

export type LanguageFn = (hljs: HLJSApi) => Language
export type CompilerExt = (mode: Mode, parent: Mode | Language | null) => void

export interface HighlightResult {
code?: string
relevance : number
value : string
language? : string
illegal : boolean
errorRaised? : Error
// * for auto-highlight
secondBest? : Omit<HighlightResult, 'second_best'>
// private
_illegalBy? : illegalData
_emitter : Emitter
_top? : Language | CompiledMode
}
export interface AutoHighlightResult extends HighlightResult {}

export interface illegalData {
message: string
context: string
index: number
resultSoFar : string
mode: CompiledMode
}

export type BeforeHighlightContext = {
code: string,
language: string,
result?: HighlightResult
}
export type PluginEvent = keyof HLJSPlugin;
export type HLJSPlugin = {
'after:highlight'?: (result: HighlightResult) => void,
'before:highlight'?: (context: BeforeHighlightContext) => void,
'after:highlightElement'?: (data: { el: Element, result: HighlightResult, text: string}) => void,
'before:highlightElement'?: (data: { el: Element, language: string}) => void,
// TODO: Old API, remove with v12
'after:highlightBlock'?: (data: { block: Element, result: HighlightResult, text: string}) => void,
'before:highlightBlock'?: (data: { block: Element, language: string}) => void,
}

interface EmitterConstructor {
new (opts: any): Emitter
}

export interface HighlightOptions {
language: string
ignoreIllegals?: boolean
}

export interface HLJSOptions {
noHighlightRe: RegExp
languageDetectRe: RegExp
classPrefix: string
cssSelector: string
languages?: string[]
__emitter: EmitterConstructor
ignoreUnescapedHTML?: boolean
throwUnescapedHTML?: boolean
}

export interface CallbackResponse {
data: Record<string, any>
ignoreMatch: () => void
isMatchIgnored: boolean
}

export type ModeCallback = (match: RegExpMatchArray, response: CallbackResponse) => void
export type Language = LanguageDetail & Partial<Mode>
export interface Mode extends ModeCallbacks, ModeDetails {}

export interface LanguageDetail {
name?: string
unicodeRegex?: boolean
rawDefinition?: () => Language
aliases?: string[]
disableAutodetect?: boolean
contains: (Mode)[]
case_insensitive?: boolean
keywords?: Record<string, any> | string
isCompiled?: boolean,
exports?: any,
classNameAliases?: Record<string, string>
compilerExtensions?: CompilerExt[]
supersetOf?: string
}

// technically private, but exported for convenience as this has
// been a pretty stable API and is quite useful
export interface Emitter {
addKeyword(text: string, kind: string): void
addText(text: string): void
toHTML(): string
finalize(): void
closeAllNodes(): void
openNode(kind: string): void
closeNode(): void
addSublanguage(emitter: Emitter, subLanguageName: string): void
}

export type HighlightedHTMLElement = HTMLElement & {result?: object, secondBest?: object, parentNode: HTMLElement}

/* modes */

interface ModeCallbacks {
"on:end"?: Function,
"on:begin"?: ModeCallback
}

export interface CompiledLanguage extends LanguageDetail, CompiledMode {
isCompiled: true
contains: CompiledMode[]
keywords: Record<string, any>
}

export type CompiledScope = Record<number, string> & {_emit?: Record<number, boolean>, _multi?: boolean, _wrap?: string};

export type CompiledMode = Omit<Mode, 'contains'> &
{
begin?: RegExp | string
end?: RegExp | string
scope?: string
contains: CompiledMode[]
keywords: KeywordDict
data: Record<string, any>
terminatorEnd: string
keywordPatternRe: RegExp
beginRe: RegExp
endRe: RegExp
illegalRe: RegExp
matcher: any
isCompiled: true
starts?: CompiledMode
parent?: CompiledMode
beginScope?: CompiledScope
endScope?: CompiledScope
}

interface ModeDetails {
begin?: RegExp | string | (RegExp | string)[]
match?: RegExp | string | (RegExp | string)[]
end?: RegExp | string | (RegExp | string)[]
// deprecated in favor of `scope`
className?: string
scope?: string | Record<number, string>
beginScope?: string | Record<number, string>
endScope?: string | Record<number, string>
contains?: ("self" | Mode)[]
endsParent?: boolean
endsWithParent?: boolean
endSameAsBegin?: boolean
skip?: boolean
excludeBegin?: boolean
excludeEnd?: boolean
returnBegin?: boolean
returnEnd?: boolean
__beforeBegin?: Function
parent?: Mode
starts?:Mode
lexemes?: string | RegExp
keywords?: Record<string, any> | string
beginKeywords?: string
relevance?: number
illegal?: string | RegExp | Array<string | RegExp>
variants?: Mode[]
cachedVariants?: Mode[]
// parsed
subLanguage?: string | string[]
isCompiled?: boolean
label?: string
}

declare const hljs : HLJSApi;
export default hljs;
13 changes: 13 additions & 0 deletions frontend/imports/highlightjs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// @ts-ignore
import hljs from "https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/es/highlight.min.js"
// @ts-ignore
import hljs_julia from "https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/es/languages/julia.min.js"
// @ts-ignore
import hljs_juliarepl from "https://cdn.jsdelivr.net/gh/highlightjs/[email protected]/build/es/languages/julia-repl.min.js"

hljs.registerLanguage("julia", hljs_julia)
hljs.registerLanguage("julia-repl", hljs_juliarepl)
// https://github.com/highlightjs/highlight.js/pull/3432
hljs.registerAliases(["jldoctest"], { languageName: "julia-repl" })

export default hljs

0 comments on commit d851168

Please sign in to comment.