From 781e2767be63015fc957bf59c7e4bad341bce66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 28 Feb 2019 12:50:50 +0100 Subject: [PATCH 01/46] Export webmain to use from typescript --- madoko.d.ts | 38 +++++++++++++++++++++++++++++++++++++ package.json | 53 +++++++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 25 deletions(-) create mode 100644 madoko.d.ts diff --git a/madoko.d.ts b/madoko.d.ts new file mode 100644 index 00000000..b0598334 --- /dev/null +++ b/madoko.d.ts @@ -0,0 +1,38 @@ +declare module 'madoko' { + type MadokoOptions = any; + + //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () + type MadokoCallback = + (md: string, + stdout: string, + needRerun: boolean, + options: MadokoOptions, + files: string, + filesRefer: string, + filesWrite: string, + labels: string, + links: string, + customs: string, + entities: string) => any; + + export function addImage(embeds : any, imageName : string, data : string): any; + + export function clearStorage(): any; + + export function readTextFile(fname : string): string; + + export function unlinkFile(fname : string): any; + + export function writeTextFile(fileName : string, content : string): any; + + export function initialOptions(args?: string): MadokoOptions; + + export function markdown( + inputName: string, + input: string, + outdir: string, + options: MadokoOptions, + modes: string, + convertTex: boolean, + cont: MadokoCallback): any; +} diff --git a/package.json b/package.json index ac159b7c..ef053f30 100644 --- a/package.json +++ b/package.json @@ -1,24 +1,26 @@ { - "name" : "madoko", - "author" : "Daan Leijen, Microsoft Corp.", - "version" : "1.1.6", - "homepage" : "http://madoko.codeplex.com", - "description" : "Madoko is a fast scholarly Markdown processor written in Koka", - "licenses": [{ - "type" : "Apache License 2.0", - "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" - }], + "name": "madoko", + "author": "Daan Leijen, Microsoft Corp.", + "version": "1.1.6", + "homepage": "http://madoko.codeplex.com", + "description": "Madoko is a fast scholarly Markdown processor written in Koka", + "licenses": [ + { + "type": "Apache License 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], "keywords": [ - "Madoko", - "Markdown", - "LaTeX", - "Koka", - "Microsoft", - "Scholarly", - "Academic" + "Madoko", + "Markdown", + "LaTeX", + "Koka", + "Microsoft", + "Scholarly", + "Academic" ], "preferGlobal": true, - "main": "./lib/madoko.js", + "main": "./lib/webmain.js", "bin": { "madoko": "./lib/cli.js" }, @@ -33,19 +35,20 @@ "readme.md", "license.txt" ], - "engines" : { - "node": ">=0.10.0" + "types": "madoko.d.ts", + "engines": { + "node": ">=0.10.0" }, "dependencies": { - "amdefine" : ">=0.0.4", - "requirejs" : ">=2.1", - "mkdirp" : ">=0.3.5" + "amdefine": ">=0.0.4", + "requirejs": ">=2.1", + "mkdirp": ">=0.3.5" }, "devDependencies": { - "jake" : ">=0.7.6" + "jake": ">=0.7.6" }, "repository": { - "type": "hg", - "url" : "https://hg.codeplex.com/madoko" + "type": "hg", + "url": "https://hg.codeplex.com/madoko" } } From 359c85301615273e8d52ce2a346d19e918f3e3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 28 Feb 2019 12:52:43 +0100 Subject: [PATCH 02/46] Export more information about labels --- src/definitions.kk | 2 +- src/inline.kk | 1 + src/madoko.kk | 30 +++++++++++++++++++++++++++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/definitions.kk b/src/definitions.kk index 6226a1cd..00ad5a5e 100644 --- a/src/definitions.kk +++ b/src/definitions.kk @@ -138,7 +138,7 @@ function parseDefinitionsFull( // set label if (attrsx.name != "") then { val labelCaption = attrsx.hasKey("caption").maybe("",id) - labels[attrsx.name] := Label(bname,attrsx.label,labelCaption) + labels[attrsx.name] := Label(bname,attrsx.label,labelCaption,attrsx) match (attrsx.hasKey("cite-label")) { Nothing -> () Just(clabel) -> { diff --git a/src/inline.kk b/src/inline.kk index 32be4724..27161fa9 100644 --- a/src/inline.kk +++ b/src/inline.kk @@ -220,6 +220,7 @@ public function parseLineInfo( lineInfo : string ) : (string,int) { val rxDataLine = regex( @"^(.*:)?(\d+)$") +// Here the current position is available. function formatInlineAcc( context : inlineContext, acc : builder, txt : string, lineNo : int, linePrefix : string ) : st string { diff --git a/src/madoko.kk b/src/madoko.kk index 6ffc9561..08f61a51 100644 --- a/src/madoko.kk +++ b/src/madoko.kk @@ -99,7 +99,15 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm if (fmt.isFmtHtml) { fcontext.inlineContext.labels.list().foreach fun(elem) { val (name,label) = elem - log("labels","{ \"name\": " + name.json + ", \"text\": " + label.labelText.json + ", \"caption\": " + label.labelCaption.json + " }" ); + val labelName = "\"name\": " + name.json + val labelPos = match(label.labelPosition) { + Just((path, lineno)) -> ", \"position\": {\"path\": \"" + path + "\", \"line\":" + lineno.show + "}" + Nothing -> "" + } + val element = ", \"element\": " + label.element.json + val text = ", \"text\": " + label.labelText.json + val caption = ", \"caption\": " + label.labelCaption.json + log("labels","{" + labelName + labelPos + element + text + caption + " }"); } fcontext.inlineContext.links.list().foreach fun(elem) { val (name,link) = elem @@ -109,6 +117,7 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm logMetadata(options.metadata); } + // generate full html/tex if needed val res = if (xfull) then (fmt.pick(fmtHtmlFull,fmtLatexFull))(body,options,fcontext.inlineContext.metadata) @@ -121,6 +130,25 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm (res,options) } +function labelPosition(label : label) : maybe<(string, int)> { + match(label.labelAttrs.hasKey("data-line")) { + Just(info) -> { + val (path, lineno) = parseLineInfo(info) + Just((path.lastPathSegment, lineno)) + // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) + } + Nothing -> Nothing + } +} + +public function lastPathSegment( lineInfo : string ) : string { + match (lineInfo.find(rxLastPath)) { + Nothing -> lineInfo + Just(cap) -> cap.groups[1] + } +} +val rxLastPath = regex( @";([^:;]+):$") + // Export initial options for JavaScript usage public val initialOptions0 = initialOptions(); From 3e479727a6fc8aeeb9712b25007014c835d0a6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 28 Feb 2019 15:40:33 +0100 Subject: [PATCH 03/46] Backport null --- src/common.kk | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/common.kk b/src/common.kk index 81e043f4..92d9e517 100644 --- a/src/common.kk +++ b/src/common.kk @@ -14,6 +14,24 @@ import std/regex import std/dict import std/path +// backporting null from newer Koka versions: + +// Abstract type used for passing `null` values to external functions +public type null + +// Transform a `:maybe` type to a `:null` type (using `null` for `Nothing`). +public external null(x : maybe) : null { + cs inline "(#1.tag_ == __std_core._maybe_Tag.Nothing ? default(##1) : #1.@value)" + js inline "(#1==null ? null : #1.unJust)" +} + +// Transform a `:null` type to a `:maybe` type. Note that it is not +// always the case that `id(x) == maybe(null(x))` (e.g. when `x = Just(Nothing)`). +public external maybe( n : null ) : maybe { + cs inline "(EqualityComparer<##1>.Default.Equals(#1,default(##1)) ? __std_core._maybe<##1>.Nothing_ : new __std_core._maybe<##1>(#1))" + js inline "(#1==null ? $std_core.Nothing : $std_core.Just(#1))" +} + // Warning messages get logged public function warning( message : string, logname : string = "warning" ) : () { log(logname," warning: " + message) From a7df281a97e153924b238629ae42f4572ca1224e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 28 Feb 2019 15:41:54 +0100 Subject: [PATCH 04/46] Strip down processing --- madoko.d.ts | 31 ++++++++++++++---- src/driver.kk | 86 +++++++++++++++++++++++++++++++++++++++++++++++++- src/madoko.kk | 2 +- src/webmain.kk | 57 +++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+), 8 deletions(-) diff --git a/madoko.d.ts b/madoko.d.ts index b0598334..b1f6fb47 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -1,12 +1,24 @@ declare module 'madoko' { - type MadokoOptions = any; + type Options = any; + type Position = { path: string, line: number } + type Label = { + id: string, + element: string, + caption: string, + position?: Position + } + type DocumentInfo = { + lineMap: any, + labels: Label[], + context: any + } //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () - type MadokoCallback = + type Callback = (md: string, stdout: string, needRerun: boolean, - options: MadokoOptions, + options: Options, files: string, filesRefer: string, filesWrite: string, @@ -25,14 +37,21 @@ declare module 'madoko' { export function writeTextFile(fileName : string, content : string): any; - export function initialOptions(args?: string): MadokoOptions; + export function initialOptions(args?: string): Options; export function markdown( inputName: string, input: string, outdir: string, - options: MadokoOptions, + options: Options, modes: string, convertTex: boolean, - cont: MadokoCallback): any; + cont: Callback): any; + + export function analyze( + inputName: string, + content: string, + outdir: string, + options: Options, + callback: (DocumentInfo) => any): any; } diff --git a/src/driver.kk b/src/driver.kk index 4f9b031d..cd4f2ed6 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -27,6 +27,8 @@ import mathStatic import texParser import includes import bibliography +import formatBlock // just the formatContext +import definitions // for parseBody public struct runners( runPdfLatex : ( srcFile : string, texFile : string, opts : options, content : string, continue : (int) -> io () ) -> io (), @@ -420,4 +422,86 @@ public function withLogCompress( name: string, action : () -> a ) : io () +) : io () { + val styleDir = opts.installDir + "/../styles" + val searchDirs = [inName.dirname,outName.dirname,styleDir] + val opts0 = opts.options + + content.include(False, inName, outName, searchDirs, opts0) fun(icontent0,lmap) { + // remove madoko comments + val icontent = icontent0.removeMadokoComments + + // set up options + val date = now() + val opts1 = opts0(lineMap=lmap, + processTimeout=if (opts0.sandbox) then 60000 else opts0.processTimeout, + metadata=opts0.metadata + + [("docname",inName.stemname),("filename",inName)] + + [("madoko-version",opts0.version)] + + [("date",date.isoLocalDate),("time",date.isoLocalTime.substr(0,5)), + ("year",date.year.show),("month",date.month.show2),("day",date.day.show2), + ("hours",date.hours.show2),("minutes",date.minutes.show2),("seconds",date.seconds.show2)] ) + + // extract default cite-style if it exists + val opts2 = opts1.extractCitestyle(outName) + + val mmopts = opts2.parseMeta( FmtHtml, icontent.normalizeSource ).fst // get bibdata,bibstyle,mathimg + + // TODO remove some of these for LSP mode + + // always read dims: even in dynamic mode some pdf math may exist + val dims = if (mmopts.rebuild) then "" else outName.changeExt(".dimx").readTextFileDef("",False) + val (mdim,svgdefs,_) = dims.parseMathDim(mmopts.math) + + val xopts = opts2(math=(opts2.math)(dim=mdim,svgDefs=svgdefs)) + val mopts = mmopts(math=(mmopts.math)(dim=mdim,svgDefs=svgdefs)) + + // register languages for highlighting + registerColorizers(mopts, searchDirs, icontent); + + // copy early since async latex may start for math + if (mopts.copyStyles) { // && !(xopts.sandbox) // allow even in sandbox + // if (mopts.embedLimit < 16*1024) { // only copy standard css style if it will not be embedded + tryCopyTextFileFromNoSandboxTo( "madoko.css", styleDir, outName.dirname ) + } + + processLSP(icontent, xopts, continue) + } +} + +function processLSP(content : string, xopts0 : options, continue : (formatContext) -> io ()) : io () { + + // Just extract metadata and symbols (copied from markdownNormal) + // first normalize the input: all tabs to 4 spaces. + val fmt = FmtHtml + val (options1, src) = parseMeta(xopts0, fmt, content.normalizeSource) + val options = options1(metadata = options1.metadata.completeAuthorKeys) + val xfull = options.full.maybe(True,id) + + val icontext = inlineContext(fmt,options.metadata.dict, + options.math.dim, + options.embedinfos, + options.citestyle.maybe(citeNumeric,id), + options.sanitize,options.bench,options.verbose, + options.math.mode.isStatic, + options.highlight,options.starBold,options.sandbox,options.prettyAlign) + + val (_, fcontext) = + parseBody( + initialFormatContext(icontext,options.lineMap, options.headingBase,options.pedantic, fmt), + options.lineNo, + src, + options.metadata, options.tocDepth, + options.sectionBase,options.sectionMax) + + continue(fcontext) +} diff --git a/src/madoko.kk b/src/madoko.kk index 08f61a51..a359f0ae 100644 --- a/src/madoko.kk +++ b/src/madoko.kk @@ -171,7 +171,7 @@ function logMetadata( mdata : metadata ) { // Create an ":inlineContext" -function inlineContext( fmt : formatter, +public function inlineContext( fmt : formatter, metadata : dict, mathinfos : dict, embedinfos : dict, diff --git a/src/webmain.kk b/src/webmain.kk index aff66353..cfd3dbb8 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -17,6 +17,25 @@ import options import driver import storage import mathStatic +import formatBlock // just the formatContext +import inline // for inlineContext +import std/regex + +public struct position (path: string, line: int) + +public struct labeledElement ( + id: string, + element: string, + caption: string, + position: null +) + +public struct analyzeResults ( + lineMap : lineMap, + labels: vector, + // just for debugging purposes we also include the original formatContext + context : formatContext +) public function initialOptions( args : string = "" ) : io options { val opts = if (args=="") then Options(version=version/version) @@ -128,3 +147,41 @@ public function markdown( inputName : string, input : string, outdir : string, o } () } + +public function analyze( + inputName : string, content : string, outdir : string, + opts : options, + continue : (analyzeResults) -> io () +) : io () { + val copts = coptions(options = opts, outputDir = outdir, convertTex = False) + val outName = outputName(inputName, copts) + processContentLSP(inputName, outName, content, copts) fun (ctx) { + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), ctx) + continue(res) + } +} + +function labelInfo(elem: (string, label)): labeledElement { + val (id, label) = elem + LabeledElement(id, label.element, label.labelCaption, null(label.labelPosition)) +} + +function labelPosition(label : label) : maybe { + match(label.labelAttrs.hasKey("data-line")) { + Just(info) -> { + val (path, lineno) = parseLineInfo(info) + Just(Position(path.lastPathSegment, lineno)) + // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) + } + Nothing -> Nothing + } +} + +function lastPathSegment( lineInfo : string ) : string { + match (lineInfo.find(rxLastPath)) { + Nothing -> lineInfo + Just(cap) -> cap.groups[1] + } +} +val rxLastPath = regex( @";([^:;]+):$" ) \ No newline at end of file From deb8cde42ceb3592bc5672df9871d97933f75174 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Tue, 5 Mar 2019 19:42:54 +0100 Subject: [PATCH 05/46] Log warnings in analyze mode --- madoko.d.ts | 3 ++- src/driver.kk | 17 ++++++++++++----- src/webmain.kk | 21 +++++++++++++++------ 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/madoko.d.ts b/madoko.d.ts index b1f6fb47..29f6e3f9 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -10,7 +10,8 @@ declare module 'madoko' { type DocumentInfo = { lineMap: any, labels: Label[], - context: any + context: any, + log: string } //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () diff --git a/src/driver.kk b/src/driver.kk index cd4f2ed6..86b2d7b7 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -487,21 +487,28 @@ function processLSP(content : string, xopts0 : options, continue : (formatContex val options = options1(metadata = options1.metadata.completeAuthorKeys) val xfull = options.full.maybe(True,id) - val icontext = inlineContext(fmt,options.metadata.dict, + val icontext = inlineContext(fmt, options.metadata.dict, options.math.dim, options.embedinfos, options.citestyle.maybe(citeNumeric,id), options.sanitize,options.bench,options.verbose, options.math.mode.isStatic, - options.highlight,options.starBold,options.sandbox,options.prettyAlign) - - val (_, fcontext) = + options.highlight,options.starBold,options.sandbox,options.prettyAlign) + + val (warns0, (_, fcontext)) = withLog("warning") { parseBody( initialFormatContext(icontext,options.lineMap, options.headingBase,options.pedantic, fmt), options.lineNo, src, options.metadata, options.tocDepth, options.sectionBase,options.sectionMax) - + } + // TODO dont collect warnings stringly typed. + log("stdout", warns0) + // val warns = fixWarnings(warns0) + // if (warns != "") { + // log("stdout", warns) + // } + continue(fcontext) } diff --git a/src/webmain.kk b/src/webmain.kk index cfd3dbb8..1fd8df43 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -34,7 +34,8 @@ public struct analyzeResults ( lineMap : lineMap, labels: vector, // just for debugging purposes we also include the original formatContext - context : formatContext + context : formatContext, + log: string ) public function initialOptions( args : string = "" ) : io options { @@ -148,18 +149,26 @@ public function markdown( inputName : string, input : string, outdir : string, o () } +// Currently the logs are very sparse... Did I omit too much of the +// processing to catch errors? public function analyze( inputName : string, content : string, outdir : string, opts : options, continue : (analyzeResults) -> io () ) : io () { + printRedirect( fun(s) { log("stdout", s) }); + val copts = coptions(options = opts, outputDir = outdir, convertTex = False) val outName = outputName(inputName, copts) - processContentLSP(inputName, outName, content, copts) fun (ctx) { - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), ctx) - continue(res) + withLog("stdout") { + processContentLSP(inputName, outName, content, copts) fun (ctx) { + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), ctx, stdout) + continue(res) + } } + () } function labelInfo(elem: (string, label)): labeledElement { @@ -168,7 +177,7 @@ function labelInfo(elem: (string, label)): labeledElement { } function labelPosition(label : label) : maybe { - match(label.labelAttrs.hasKey("data-line")) { + match(label.labelAttrs.hasKey("data-line")) { // || label.labelAttrs.hasKey("data-line-first") Just(info) -> { val (path, lineno) = parseLineInfo(info) Just(Position(path.lastPathSegment, lineno)) From 5837a88e24e98eb1a99513f307950462529f97d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 7 Mar 2019 12:48:52 +0100 Subject: [PATCH 06/46] Allow block extraction in public interface --- madoko.d.ts | 14 ++++++++++ src/driver.kk | 8 +++--- src/webmain.kk | 70 +++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 7 deletions(-) diff --git a/madoko.d.ts b/madoko.d.ts index 29f6e3f9..90ab14e2 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -10,9 +10,23 @@ declare module 'madoko' { type DocumentInfo = { lineMap: any, labels: Label[], + blocks: Block[], context: any, log: string } + type Block = { + kind: string, + // the element id, empty if not present + id: string, + // the tag name, empty if not present + name: string, + // the child nodes + content: Block[], + // the element classes + classes: string[], + // the annotated attributes + attributes: any + } //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () type Callback = diff --git a/src/driver.kk b/src/driver.kk index 86b2d7b7..d247ff25 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -430,7 +430,7 @@ val rxDataLinesPre = regex("(?:\r?\n%mdk-data-line=.*(?=\r?\n%mdk-data-line))+") public function processContentLSP( inName : string, outName : string, content : string, opts : commandOptions, - continue : (formatContext) -> io () + continue : (list, formatContext) -> io () ) : io () { val styleDir = opts.installDir + "/../styles" val searchDirs = [inName.dirname,outName.dirname,styleDir] @@ -478,7 +478,7 @@ public function processContentLSP( } } -function processLSP(content : string, xopts0 : options, continue : (formatContext) -> io ()) : io () { +function processLSP(content : string, xopts0 : options, continue : (list, formatContext) -> io ()) : io () { // Just extract metadata and symbols (copied from markdownNormal) // first normalize the input: all tabs to 4 spaces. @@ -495,7 +495,7 @@ function processLSP(content : string, xopts0 : options, continue : (formatContex options.math.mode.isStatic, options.highlight,options.starBold,options.sandbox,options.prettyAlign) - val (warns0, (_, fcontext)) = withLog("warning") { + val (warns0, (blocks, fcontext)) = withLog("warning") { parseBody( initialFormatContext(icontext,options.lineMap, options.headingBase,options.pedantic, fmt), options.lineNo, @@ -510,5 +510,5 @@ function processLSP(content : string, xopts0 : options, continue : (formatContex // log("stdout", warns) // } - continue(fcontext) + continue(blocks, fcontext) } diff --git a/src/webmain.kk b/src/webmain.kk index 1fd8df43..7978a480 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -17,10 +17,12 @@ import options import driver import storage import mathStatic +import block // just for the type block import formatBlock // just the formatContext import inline // for inlineContext import std/regex +// TODO change to { path, from, to } to express ranges public struct position (path: string, line: int) public struct labeledElement ( @@ -33,11 +35,69 @@ public struct labeledElement ( public struct analyzeResults ( lineMap : lineMap, labels: vector, + blocks: vector, // just for debugging purposes we also include the original formatContext - context : formatContext, + context : formatContext, log: string ) +// the type of block nodes we export to js / typescript +public struct blockElement ( + // the block kind + kind: string, + // the element id, empty if not present + id: string, + // the tag name, empty if not present + name: string, + // the child nodes + content: vector, + // the text content + text: string, + // the classes + classes: vector, + // attributes of the block node (like "caption") + attributes: dict + // TODO also include position and range information. +) + +function toBlockElements(bs: list): div vector { + vector(bs.map(toBlockElement).concatMaybe) +} + +// TODO also include range information in extracted block structure +function toBlockElement(b: block): div maybe { + match(b) { + HLine( attrs ) -> + Just(BlockElement("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals))) + Para( text, attrs ) -> + Just(BlockElement("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Code( text, _, attrs) -> + Just(BlockElement("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Quote( content, attrs) -> + Just(BlockElement("quote", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + List( _, content, attrs) -> + Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Item( content, attrs) -> + Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Heading( _, text, attrs) -> + Just(BlockElement("list", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + // currently table cells are ignored + Table( _, _, _, attrs) -> + Just(BlockElement("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals))) + Div( content, attrs) -> + Just(BlockElement("div", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Source( text, _, attrs) -> + Just(BlockElement("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + // DefLink( id, link) -> + // DefFootnote( id, content) -> + // Empty() -> + // Special( name, value, attrs) -> + // Line( text, _, attrs) -> + _ -> Nothing + } +} + + public function initialOptions( args : string = "" ) : io options { val opts = if (args=="") then Options(version=version/version) else match(parseOptions(version/version,args)) { @@ -161,16 +221,20 @@ public function analyze( val copts = coptions(options = opts, outputDir = outdir, convertTex = False) val outName = outputName(inputName, copts) withLog("stdout") { - processContentLSP(inputName, outName, content, copts) fun (ctx) { + processContentLSP(inputName, outName, content, copts) fun (blocks, ctx) { val stdout = getLog("stdout") val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), ctx, stdout) + val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), blocks.toBlockElements, ctx, stdout) continue(res) } } () } +public function parseBlocks(text: string): div vector { + parseBlocks(text, 0, End, mdata = dict()).toBlockElements +} + function labelInfo(elem: (string, label)): labeledElement { val (id, label) = elem LabeledElement(id, label.element, label.labelCaption, null(label.labelPosition)) From 7e3a36bb96174de6a1eacebfbdfffe3d17a4df3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 7 Mar 2019 12:52:32 +0100 Subject: [PATCH 07/46] Add comment --- src/webmain.kk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/webmain.kk b/src/webmain.kk index 7978a480..22217c7d 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -231,6 +231,11 @@ public function analyze( () } + +// Sometimes we don't want to process the full document but only +// obtain the document structure of the currently open editor. +// Sadly, parseBlocks does not provide enough positioning information +// on the blocks ATM. public function parseBlocks(text: string): div vector { parseBlocks(text, 0, End, mdata = dict()).toBlockElements } From dc2841cacc2fd8358fa6ae4c257b0d2d0628d2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 7 Mar 2019 14:55:16 +0100 Subject: [PATCH 08/46] Also annotate range information on blocks --- src/block.kk | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/block.kk b/src/block.kk index ce175116..19e03c40 100644 --- a/src/block.kk +++ b/src/block.kk @@ -510,23 +510,37 @@ function parseBlocksAccLine( context : blockContext, acc : list, line : { if (src=="") return acc; val (block,next,matched) = matchRules(context.grammar,context(lineNo=line),src,raw) - val line2 = line + matched.count("\n") - val ofs = match(matched.find(rxPreWhite)) { - Nothing -> 0 - Just(cap) -> cap.regex/matched.count("\n") - } + val lines = matched.count("\n") + + // Adjust start and end line and exclude preceding and trailing empty lines + val start = line + matched.linesMatching(rxPreWhite) + val end = line + lines - matched.linesMatching(rxPostWhite) val block2 = block.adjustAttrs( fun(attrs:attrs) { - attrs.setLineNo(context.lineMap,line+ofs) + attrs.setLineNo(context.lineMap, start).setRange(context.lineMap, start, end) }) - parseBlocksAccLine( context, Cons(block2,acc), line2, src.substr1(next) ) + parseBlocksAccLine( context, Cons(block2,acc), line + lines, src.substr1(next) ) } val rxPreWhite = regex(@"^\s+") +val rxPostWhite = regex(@"\s+$") +function linesMatching(s : string, r : regex): int { + match(s.find(r)) { + Nothing -> 0 + Just(cap) -> cap.regex/matched.count("\n") + } +} function raw( s : string ) : block { Line(s) } +public function setRange( attrs: attrs, lineMap: lineMap, start: int, end: int) : attrs { + // TODO annotating the element in an attribute to later parse it back seems wrong. + // also the file on both should be the same! + attrs.addKeyval("data-line-start", translateLine(lineMap, start)) + attrs.addKeyval("data-line-end", translateLine(lineMap, end)) +} + public function setLineNo( attrs: attrs, lineMap: lineMap, lineNo : int, overwrite : bool = False ) : attrs { val srcline = translateLine(lineMap,lineNo) val attrs1 = if (!overwrite && attrs.hasKey("data-line").bool) then attrs else attrs.setLineNo(lineNo,srcline) From 8e1607cbc435c6f44d848ed006db87ce7916dcda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 7 Mar 2019 14:58:18 +0100 Subject: [PATCH 09/46] Fix some copy&paste errors --- src/webmain.kk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webmain.kk b/src/webmain.kk index 22217c7d..3313b622 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -78,9 +78,9 @@ function toBlockElement(b: block): div maybe { List( _, content, attrs) -> Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) Item( content, attrs) -> - Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("item", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) Heading( _, text, attrs) -> - Just(BlockElement("list", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) // currently table cells are ignored Table( _, _, _, attrs) -> Just(BlockElement("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals))) From 92781d550aaa6ba64a7f8e745f6a48800e95ae69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Thu, 7 Mar 2019 15:39:58 +0100 Subject: [PATCH 10/46] Also export ranges of blocks --- madoko.d.ts | 5 ++++- src/block.kk | 3 +-- src/common.kk | 13 +++++++++++ src/webmain.kk | 59 +++++++++++++++++++++++++++++++++++--------------- 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/madoko.d.ts b/madoko.d.ts index 90ab14e2..5478d9d4 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -1,6 +1,7 @@ declare module 'madoko' { type Options = any; type Position = { path: string, line: number } + type Range = { path: string, from: number, to: number } type Label = { id: string, element: string, @@ -25,7 +26,9 @@ declare module 'madoko' { // the element classes classes: string[], // the annotated attributes - attributes: any + attributes: any, + // the range (lines are 1-based and inclusive) + range?: Range } //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () diff --git a/src/block.kk b/src/block.kk index 19e03c40..9fef0f39 100644 --- a/src/block.kk +++ b/src/block.kk @@ -537,8 +537,7 @@ function raw( s : string ) : block { public function setRange( attrs: attrs, lineMap: lineMap, start: int, end: int) : attrs { // TODO annotating the element in an attribute to later parse it back seems wrong. // also the file on both should be the same! - attrs.addKeyval("data-line-start", translateLine(lineMap, start)) - attrs.addKeyval("data-line-end", translateLine(lineMap, end)) + attrs.addKeyval("data-line-start", translateLine(lineMap, start)).addKeyval("data-line-end", translateLine(lineMap, end)) } public function setLineNo( attrs: attrs, lineMap: lineMap, lineNo : int, overwrite : bool = False ) : attrs { diff --git a/src/common.kk b/src/common.kk index 92d9e517..b0c1a0dd 100644 --- a/src/common.kk +++ b/src/common.kk @@ -32,6 +32,19 @@ public external maybe( n : null ) : maybe { js inline "(#1==null ? $std_core.Nothing : $std_core.Just(#1))" } +function unjust( m : maybe ) : exn a { + match(m) { + Just(x) -> x + } +} + +function (||)( m1 : maybe, m2: maybe ) : maybe { + match(m1) { + Nothing -> m2 + _ -> m1 + } +} + // Warning messages get logged public function warning( message : string, logname : string = "warning" ) : () { log(logname," warning: " + message) diff --git a/src/webmain.kk b/src/webmain.kk index 3313b622..d65f1f69 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -25,6 +25,9 @@ import std/regex // TODO change to { path, from, to } to express ranges public struct position (path: string, line: int) +// lines are 1-based and inclusive. +public struct range (path: string, from: int, to: int) + public struct labeledElement ( id: string, element: string, @@ -56,38 +59,58 @@ public struct blockElement ( // the classes classes: vector, // attributes of the block node (like "caption") - attributes: dict + attributes: dict, // TODO also include position and range information. + position: null ) -function toBlockElements(bs: list): div vector { - vector(bs.map(toBlockElement).concatMaybe) +function range(attr: attrs, inputName: string): maybe { + match (attr.hasKey("data-line-start")) { + Just(start) -> catch({ + val end = attr.lookupKey("data-line-end", start) + val (path, startLine) = start.extractPosition(inputName) + val (_, endLine) = end.extractPosition(inputName) + Just(Range(path, startLine, endLine)) + }, fun(exn) { Nothing }) + Nothing -> Nothing + } +} + +function extractPosition(locationString: string, inputName: string): exn (string, int) { + val locs = (inputName + ":" + locationString).split(";").list + val last = locs.last.split(":") + (last[0], last[1].parseInt.unJust) +} + +// we need the inputName to normalize position information +function toBlockElements(bs: list, inputName: string): div vector { + vector(bs.map(fun(b){ b.toBlockElement(inputName) }).concatMaybe) } // TODO also include range information in extracted block structure -function toBlockElement(b: block): div maybe { +function toBlockElement(b: block, inputName: string): div maybe { match(b) { HLine( attrs ) -> - Just(BlockElement("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Para( text, attrs ) -> - Just(BlockElement("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Code( text, _, attrs) -> - Just(BlockElement("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Quote( content, attrs) -> - Just(BlockElement("quote", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("quote", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) List( _, content, attrs) -> - Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Item( content, attrs) -> - Just(BlockElement("item", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("item", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Heading( _, text, attrs) -> - Just(BlockElement("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) // currently table cells are ignored Table( _, _, _, attrs) -> - Just(BlockElement("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Div( content, attrs) -> - Just(BlockElement("div", attrs.name, attrs.elem, content.toBlockElements, "", vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("div", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Source( text, _, attrs) -> - Just(BlockElement("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals))) + Just(BlockElement("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) // DefLink( id, link) -> // DefFootnote( id, content) -> // Empty() -> @@ -224,7 +247,7 @@ public function analyze( processContentLSP(inputName, outName, content, copts) fun (blocks, ctx) { val stdout = getLog("stdout") val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), blocks.toBlockElements, ctx, stdout) + val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), blocks.toBlockElements(inputName), ctx, stdout) continue(res) } } @@ -236,9 +259,9 @@ public function analyze( // obtain the document structure of the currently open editor. // Sadly, parseBlocks does not provide enough positioning information // on the blocks ATM. -public function parseBlocks(text: string): div vector { - parseBlocks(text, 0, End, mdata = dict()).toBlockElements -} +// public function parseBlocks(text: string): div vector { +// parseBlocks(text, 0, End, mdata = dict()).toBlockElements +// } function labelInfo(elem: (string, label)): labeledElement { val (id, label) = elem From f358e3a52235ee4151f1a720a4d3af24c7b75538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 12:13:20 +0100 Subject: [PATCH 11/46] Move LSP api to a separate koka-file --- Jakefile.js | 14 ++++ madoko.d.ts | 44 +---------- package.json | 2 +- src/api.kk | 187 +++++++++++++++++++++++++++++++++++++++++++++++ src/backports.kk | 32 ++++++++ src/common.kk | 33 +-------- src/webmain.kk | 158 --------------------------------------- 7 files changed, 238 insertions(+), 232 deletions(-) create mode 100644 src/api.kk create mode 100644 src/backports.kk diff --git a/Jakefile.js b/Jakefile.js index 16786026..7f4db9e3 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -142,6 +142,20 @@ task("web", [], function() { }); },{async:true}) +//----------------------------------------------------- +// Tasks: api +//----------------------------------------------------- +desc("build madoko api") +task("api", [], function() { + // fixVersion("web/client/editor.html"); + var args = Array.prototype.slice.call(arguments).join(" ") + var cmd = kokaCmd + " -v -l " + args + " " + "api" + jake.logger.log("> " + cmd); + jake.exec(cmd, {interactive: true}, function(err) { + complete(); + }); +},{async:true}) + var localTexDir = "c:/texlive/texmf-local/tex/latex/local"; desc("setup web"); task("justcopy", [], function() { diff --git a/madoko.d.ts b/madoko.d.ts index 5478d9d4..0b610968 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -2,15 +2,14 @@ declare module 'madoko' { type Options = any; type Position = { path: string, line: number } type Range = { path: string, from: number, to: number } - type Label = { + type ReferenceInfo = { id: string, element: string, caption: string, position?: Position } type DocumentInfo = { - lineMap: any, - labels: Label[], + labels: ReferenceInfo[], blocks: Block[], context: any, log: string @@ -31,45 +30,8 @@ declare module 'madoko' { range?: Range } - //(md : string, stdout : string, needRerun : bool, options : options/options, files : string, filesRefer : string, filesWrite : string, labels : string, links : string, customs : string, entities : string) -> <(io :: E)> () - type Callback = - (md: string, - stdout: string, - needRerun: boolean, - options: Options, - files: string, - filesRefer: string, - filesWrite: string, - labels: string, - links: string, - customs: string, - entities: string) => any; - - export function addImage(embeds : any, imageName : string, data : string): any; - - export function clearStorage(): any; - - export function readTextFile(fname : string): string; - - export function unlinkFile(fname : string): any; - - export function writeTextFile(fileName : string, content : string): any; - - export function initialOptions(args?: string): Options; - - export function markdown( - inputName: string, - input: string, - outdir: string, - options: Options, - modes: string, - convertTex: boolean, - cont: Callback): any; - export function analyze( inputName: string, - content: string, - outdir: string, - options: Options, + content: string, callback: (DocumentInfo) => any): any; } diff --git a/package.json b/package.json index ef053f30..3225b6a5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "Academic" ], "preferGlobal": true, - "main": "./lib/webmain.js", + "main": "./lib/api.js", "bin": { "madoko": "./lib/cli.js" }, diff --git a/src/api.kk b/src/api.kk new file mode 100644 index 00000000..63babdc6 --- /dev/null +++ b/src/api.kk @@ -0,0 +1,187 @@ +module api + +import std/dict +import std/regex +import std/log + +import backports +import version + +import block // just for the type block +import formatBlock // just for formatContext +import common // for label, lineMap, and attrs +import inline // for parseLineInfo +import driver // for outputName, processContentLSP +import options + + +// Types that are part of the public API +// ------------------------------------- + +// TODO also migrate to range +public struct position (path: string, line: int) + +// Line information is 1-based and inclusive. +public struct range (path: string, from: int, to: int) + +public struct referenceInfo ( + id: string, + element: string, + caption: string, + position: null +) + +public struct documentInfo ( + labels: vector, + blocks: vector, + // just for debugging purposes we also include the original formatContext + context : formatContext, + log: string +) + +// the type of block nodes we export to js / typescript +public struct blockInfo ( + // the block kind + kind: string, + // the element id, empty if not present + id: string, + // the tag name, empty if not present + name: string, + // the child nodes + content: vector, + // the text content + text: string, + // the classes + classes: vector, + // attributes of the block node (like "caption") + attributes: dict, + // TODO also include position and range information. + position: null +) + +// Main Entrypoint +// --------------- + +// Runs the frontend to parse and analyze the document but does not +// generate an html or tex document. +// +// Resolves includes and also processes those files. +// +// TODO Currently the logs are very sparse... Did I omit too much of the +// processing to catch errors? +public function analyze( + inputName : string, content : string, continue : (documentInfo) -> io () +) : io () { + printRedirect( fun(s) { log("stdout", s) }); + + // is this ever used? + val outDir = "/tmp/madoko" + + val opts = Options( + version=version/version, + lineNoWeb=True, + math=Mathoptions(mode = Dynamic), // don't generate math latex + embedLimit=0, + verbose=1, + copyStyles=False) + + val copts = CommandOptions(options = opts, outputDir = outDir, convertTex = False) + val outName = outputName(inputName, copts) + + withLog("stdout") { + processContentLSP(inputName, outName, content, copts) fun (blocks, ctx) { + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) + continue(res) + } + } + () +} + + +// Computing position and range information +// --------------------------------------- + +function range(attr: attrs, inputName: string): maybe { + match (attr.hasKey("data-line-start")) { + Just(start) -> catch({ + val end = attr.lookupKey("data-line-end", start) + val (path, startLine) = start.extractPosition(inputName) + val (_, endLine) = end.extractPosition(inputName) + Just(Range(path, startLine, endLine)) + }, fun(exn) { Nothing }) + Nothing -> Nothing + } +} + +function extractPosition(locationString: string, inputName: string): exn (string, int) { + val locs = (inputName + ":" + locationString).split(";").list + val last = locs.last.split(":") + (last[0], last[1].parseInt.unJust) +} + +// we need the inputName to normalize position information +function toBlockInfos(bs: list, inputName: string): div vector { + vector(bs.map(fun(b){ b.toBlockInfo(inputName) }).concatMaybe) +} + +// TODO also include range information in extracted block structure +function toBlockInfo(b: block, inputName: string): div maybe { + match(b) { + HLine( attrs ) -> + Just(BlockInfo("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Para( text, attrs ) -> + Just(BlockInfo("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Code( text, _, attrs) -> + Just(BlockInfo("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Quote( content, attrs) -> + Just(BlockInfo("quote", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + List( _, content, attrs) -> + Just(BlockInfo("list", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Item( content, attrs) -> + Just(BlockInfo("item", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Heading( _, text, attrs) -> + Just(BlockInfo("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + // currently table cells are ignored + Table( _, _, _, attrs) -> + Just(BlockInfo("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Div( content, attrs) -> + Just(BlockInfo("div", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Source( text, _, attrs) -> + Just(BlockInfo("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + // DefLink( id, link) -> + // DefFootnote( id, content) -> + // Empty() -> + // Special( name, value, attrs) -> + // Line( text, _, attrs) -> + _ -> Nothing + } +} + + +function labelInfo(elem: (string, label)): referenceInfo { + val (id, label) = elem + ReferenceInfo(id, label.element, label.labelCaption, null(label.labelPosition)) +} + +// TODO change to labelRange +function labelPosition(label : label) : maybe { + match(label.labelAttrs.hasKey("data-line")) { // || label.labelAttrs.hasKey("data-line-first") + Just(info) -> { + val (path, lineno) = parseLineInfo(info) + Just(Position(path.lastPathSegment, lineno)) + // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) + } + Nothing -> Nothing + } +} + +function lastPathSegment( lineInfo : string ) : string { + val rxLastPath = regex( @";([^:;]+):$" ) + + match (lineInfo.find(rxLastPath)) { + Nothing -> lineInfo + Just(cap) -> cap.groups[1] + } +} diff --git a/src/backports.kk b/src/backports.kk new file mode 100644 index 00000000..94d2deff --- /dev/null +++ b/src/backports.kk @@ -0,0 +1,32 @@ +module backports + +// backporting null from newer Koka versions: + +// Abstract type used for passing `null` values to external functions +public type null + +// Transform a `:maybe` type to a `:null` type (using `null` for `Nothing`). +public external null(x : maybe) : null { + cs inline "(#1.tag_ == __std_core._maybe_Tag.Nothing ? default(##1) : #1.@value)" + js inline "(#1==null ? null : #1.unJust)" +} + +// Transform a `:null` type to a `:maybe` type. Note that it is not +// always the case that `id(x) == maybe(null(x))` (e.g. when `x = Just(Nothing)`). +public external maybe( n : null ) : maybe { + cs inline "(EqualityComparer<##1>.Default.Equals(#1,default(##1)) ? __std_core._maybe<##1>.Nothing_ : new __std_core._maybe<##1>(#1))" + js inline "(#1==null ? $std_core.Nothing : $std_core.Just(#1))" +} + +function unjust( m : maybe ) : exn a { + match(m) { + Just(x) -> x + } +} + +function (||)( m1 : maybe, m2: maybe ) : maybe { + match(m1) { + Nothing -> m2 + _ -> m1 + } +} diff --git a/src/common.kk b/src/common.kk index b0c1a0dd..c97efd39 100644 --- a/src/common.kk +++ b/src/common.kk @@ -14,37 +14,6 @@ import std/regex import std/dict import std/path -// backporting null from newer Koka versions: - -// Abstract type used for passing `null` values to external functions -public type null - -// Transform a `:maybe` type to a `:null` type (using `null` for `Nothing`). -public external null(x : maybe) : null { - cs inline "(#1.tag_ == __std_core._maybe_Tag.Nothing ? default(##1) : #1.@value)" - js inline "(#1==null ? null : #1.unJust)" -} - -// Transform a `:null` type to a `:maybe` type. Note that it is not -// always the case that `id(x) == maybe(null(x))` (e.g. when `x = Just(Nothing)`). -public external maybe( n : null ) : maybe { - cs inline "(EqualityComparer<##1>.Default.Equals(#1,default(##1)) ? __std_core._maybe<##1>.Nothing_ : new __std_core._maybe<##1>(#1))" - js inline "(#1==null ? $std_core.Nothing : $std_core.Just(#1))" -} - -function unjust( m : maybe ) : exn a { - match(m) { - Just(x) -> x - } -} - -function (||)( m1 : maybe, m2: maybe ) : maybe { - match(m1) { - Nothing -> m2 - _ -> m1 - } -} - // Warning messages get logged public function warning( message : string, logname : string = "warning" ) : () { log(logname," warning: " + message) @@ -697,4 +666,4 @@ public function contains( xs : list, s : string ) : bool { public function trimLines( s : string ) : string { s.replaceAll(rxTrimLines,"") } -val rxTrimLines = regex(@"^([ \t\r]*\n)+|([ \t\r]*\n)+$") \ No newline at end of file +val rxTrimLines = regex(@"^([ \t\r]*\n)+|([ \t\r]*\n)+$") diff --git a/src/webmain.kk b/src/webmain.kk index d65f1f69..aff66353 100644 --- a/src/webmain.kk +++ b/src/webmain.kk @@ -17,109 +17,6 @@ import options import driver import storage import mathStatic -import block // just for the type block -import formatBlock // just the formatContext -import inline // for inlineContext -import std/regex - -// TODO change to { path, from, to } to express ranges -public struct position (path: string, line: int) - -// lines are 1-based and inclusive. -public struct range (path: string, from: int, to: int) - -public struct labeledElement ( - id: string, - element: string, - caption: string, - position: null -) - -public struct analyzeResults ( - lineMap : lineMap, - labels: vector, - blocks: vector, - // just for debugging purposes we also include the original formatContext - context : formatContext, - log: string -) - -// the type of block nodes we export to js / typescript -public struct blockElement ( - // the block kind - kind: string, - // the element id, empty if not present - id: string, - // the tag name, empty if not present - name: string, - // the child nodes - content: vector, - // the text content - text: string, - // the classes - classes: vector, - // attributes of the block node (like "caption") - attributes: dict, - // TODO also include position and range information. - position: null -) - -function range(attr: attrs, inputName: string): maybe { - match (attr.hasKey("data-line-start")) { - Just(start) -> catch({ - val end = attr.lookupKey("data-line-end", start) - val (path, startLine) = start.extractPosition(inputName) - val (_, endLine) = end.extractPosition(inputName) - Just(Range(path, startLine, endLine)) - }, fun(exn) { Nothing }) - Nothing -> Nothing - } -} - -function extractPosition(locationString: string, inputName: string): exn (string, int) { - val locs = (inputName + ":" + locationString).split(";").list - val last = locs.last.split(":") - (last[0], last[1].parseInt.unJust) -} - -// we need the inputName to normalize position information -function toBlockElements(bs: list, inputName: string): div vector { - vector(bs.map(fun(b){ b.toBlockElement(inputName) }).concatMaybe) -} - -// TODO also include range information in extracted block structure -function toBlockElement(b: block, inputName: string): div maybe { - match(b) { - HLine( attrs ) -> - Just(BlockElement("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Para( text, attrs ) -> - Just(BlockElement("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Code( text, _, attrs) -> - Just(BlockElement("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Quote( content, attrs) -> - Just(BlockElement("quote", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - List( _, content, attrs) -> - Just(BlockElement("list", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Item( content, attrs) -> - Just(BlockElement("item", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Heading( _, text, attrs) -> - Just(BlockElement("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - // currently table cells are ignored - Table( _, _, _, attrs) -> - Just(BlockElement("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Div( content, attrs) -> - Just(BlockElement("div", attrs.name, attrs.elem, content.toBlockElements(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Source( text, _, attrs) -> - Just(BlockElement("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - // DefLink( id, link) -> - // DefFootnote( id, content) -> - // Empty() -> - // Special( name, value, attrs) -> - // Line( text, _, attrs) -> - _ -> Nothing - } -} - public function initialOptions( args : string = "" ) : io options { val opts = if (args=="") then Options(version=version/version) @@ -231,58 +128,3 @@ public function markdown( inputName : string, input : string, outdir : string, o } () } - -// Currently the logs are very sparse... Did I omit too much of the -// processing to catch errors? -public function analyze( - inputName : string, content : string, outdir : string, - opts : options, - continue : (analyzeResults) -> io () -) : io () { - printRedirect( fun(s) { log("stdout", s) }); - - val copts = coptions(options = opts, outputDir = outdir, convertTex = False) - val outName = outputName(inputName, copts) - withLog("stdout") { - processContentLSP(inputName, outName, content, copts) fun (blocks, ctx) { - val stdout = getLog("stdout") - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = AnalyzeResults(ctx.lineMap, vector(labeledElems), blocks.toBlockElements(inputName), ctx, stdout) - continue(res) - } - } - () -} - - -// Sometimes we don't want to process the full document but only -// obtain the document structure of the currently open editor. -// Sadly, parseBlocks does not provide enough positioning information -// on the blocks ATM. -// public function parseBlocks(text: string): div vector { -// parseBlocks(text, 0, End, mdata = dict()).toBlockElements -// } - -function labelInfo(elem: (string, label)): labeledElement { - val (id, label) = elem - LabeledElement(id, label.element, label.labelCaption, null(label.labelPosition)) -} - -function labelPosition(label : label) : maybe { - match(label.labelAttrs.hasKey("data-line")) { // || label.labelAttrs.hasKey("data-line-first") - Just(info) -> { - val (path, lineno) = parseLineInfo(info) - Just(Position(path.lastPathSegment, lineno)) - // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) - } - Nothing -> Nothing - } -} - -function lastPathSegment( lineInfo : string ) : string { - match (lineInfo.find(rxLastPath)) { - Nothing -> lineInfo - Just(cap) -> cap.groups[1] - } -} -val rxLastPath = regex( @";([^:;]+):$" ) \ No newline at end of file From 6777d34d2224f4e4d5c1c46908f748510d49a0ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 13:05:55 +0100 Subject: [PATCH 12/46] Move processContent to api --- src/api.kk | 97 ++++++++++++++++++++++++++++++++++++++------------- src/driver.kk | 89 ---------------------------------------------- 2 files changed, 72 insertions(+), 114 deletions(-) diff --git a/src/api.kk b/src/api.kk index 63babdc6..71e6ca0c 100644 --- a/src/api.kk +++ b/src/api.kk @@ -3,6 +3,7 @@ module api import std/dict import std/regex import std/log +import std/path import backports import version @@ -12,7 +13,11 @@ import formatBlock // just for formatContext import common // for label, lineMap, and attrs import inline // for parseLineInfo import driver // for outputName, processContentLSP +import metadata // for parseMeta +import madoko // for normalizeSource, lastPathSegment +import definitions // for parseBody import options +import includes // Types that are part of the public API @@ -35,7 +40,7 @@ public struct documentInfo ( labels: vector, blocks: vector, // just for debugging purposes we also include the original formatContext - context : formatContext, + context : formatContext, log: string ) @@ -71,32 +76,83 @@ public struct blockInfo ( // processing to catch errors? public function analyze( inputName : string, content : string, continue : (documentInfo) -> io () -) : io () { +) : io () { printRedirect( fun(s) { log("stdout", s) }); + withLog("stdout") { + processContent(inputName, content) fun (blocks, ctx) { + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) + continue(res) + } + } + () +} + + +function processContent( + inName : string, content : string, + continue : (list, formatContext) -> io () +) : io () { // is this ever used? - val outDir = "/tmp/madoko" + val outName = "/tmp/madoko/out.mdk" + + val searchDirs = [inName.dirname] - val opts = Options( - version=version/version, + val opts0 = Options( + version=version/version, lineNoWeb=True, math=Mathoptions(mode = Dynamic), // don't generate math latex embedLimit=0, verbose=1, copyStyles=False) - val copts = CommandOptions(options = opts, outputDir = outDir, convertTex = False) - val outName = outputName(inputName, copts) - - withLog("stdout") { - processContentLSP(inputName, outName, content, copts) fun (blocks, ctx) { - val stdout = getLog("stdout") - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) - continue(res) + // running fastMode destroys positioning + val fastMode = False + + content.include(fastMode, inName, outName, searchDirs, opts0) fun(icontent0,lmap) { + // remove madoko comments + val icontent = icontent0 //.removeMadokoComments + + val opts1 = opts0(lineMap=lmap, + processTimeout=if (opts0.sandbox) then 60000 else opts0.processTimeout, + metadata=opts0.metadata) + + // Just extract metadata and symbols (copied from markdownNormal) + // first normalize the input: all tabs to 4 spaces. + val (options, src) = parseMeta(opts1, FmtHtml, icontent.normalizeSource) + + val icontext = inlineContext(FmtHtml, + options.metadata.dict, + options.math.dim, + options.embedinfos, + options.citestyle.maybe(citeNumeric, id), + options.sanitize, + options.bench, + options.verbose, + options.math.mode.isStatic, + options.highlight, + options.starBold, + options.sandbox, + options.prettyAlign) + + val (warns0, (blocks, fcontext)) = withLog("warning") { + parseBody( + initialFormatContext(icontext, options.lineMap, options.headingBase, options.pedantic, FmtHtml), + options.lineNo, + src, + options.metadata, options.tocDepth, options.sectionBase, options.sectionMax) } + // TODO don't collect warnings stringly typed. + log("stdout", warns0) + // val warns = fixWarnings(warns0) + // if (warns != "") { + // log("stdout", warns) + // } + + continue(blocks, fcontext) } - () } @@ -173,15 +229,6 @@ function labelPosition(label : label) : maybe { Just(Position(path.lastPathSegment, lineno)) // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) } - Nothing -> Nothing - } -} - -function lastPathSegment( lineInfo : string ) : string { - val rxLastPath = regex( @";([^:;]+):$" ) - - match (lineInfo.find(rxLastPath)) { - Nothing -> lineInfo - Just(cap) -> cap.groups[1] + Nothing -> Nothing } } diff --git a/src/driver.kk b/src/driver.kk index d247ff25..7b3d9dd5 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -423,92 +423,3 @@ public function withLogCompress( name: string, action : () -> a ) : , formatContext) -> io () -) : io () { - val styleDir = opts.installDir + "/../styles" - val searchDirs = [inName.dirname,outName.dirname,styleDir] - val opts0 = opts.options - - content.include(False, inName, outName, searchDirs, opts0) fun(icontent0,lmap) { - // remove madoko comments - val icontent = icontent0.removeMadokoComments - - // set up options - val date = now() - val opts1 = opts0(lineMap=lmap, - processTimeout=if (opts0.sandbox) then 60000 else opts0.processTimeout, - metadata=opts0.metadata - + [("docname",inName.stemname),("filename",inName)] - + [("madoko-version",opts0.version)] - + [("date",date.isoLocalDate),("time",date.isoLocalTime.substr(0,5)), - ("year",date.year.show),("month",date.month.show2),("day",date.day.show2), - ("hours",date.hours.show2),("minutes",date.minutes.show2),("seconds",date.seconds.show2)] ) - - // extract default cite-style if it exists - val opts2 = opts1.extractCitestyle(outName) - - val mmopts = opts2.parseMeta( FmtHtml, icontent.normalizeSource ).fst // get bibdata,bibstyle,mathimg - - // TODO remove some of these for LSP mode - - // always read dims: even in dynamic mode some pdf math may exist - val dims = if (mmopts.rebuild) then "" else outName.changeExt(".dimx").readTextFileDef("",False) - val (mdim,svgdefs,_) = dims.parseMathDim(mmopts.math) - - val xopts = opts2(math=(opts2.math)(dim=mdim,svgDefs=svgdefs)) - val mopts = mmopts(math=(mmopts.math)(dim=mdim,svgDefs=svgdefs)) - - // register languages for highlighting - registerColorizers(mopts, searchDirs, icontent); - - // copy early since async latex may start for math - if (mopts.copyStyles) { // && !(xopts.sandbox) // allow even in sandbox - // if (mopts.embedLimit < 16*1024) { // only copy standard css style if it will not be embedded - tryCopyTextFileFromNoSandboxTo( "madoko.css", styleDir, outName.dirname ) - } - - processLSP(icontent, xopts, continue) - } -} - -function processLSP(content : string, xopts0 : options, continue : (list, formatContext) -> io ()) : io () { - - // Just extract metadata and symbols (copied from markdownNormal) - // first normalize the input: all tabs to 4 spaces. - val fmt = FmtHtml - val (options1, src) = parseMeta(xopts0, fmt, content.normalizeSource) - val options = options1(metadata = options1.metadata.completeAuthorKeys) - val xfull = options.full.maybe(True,id) - - val icontext = inlineContext(fmt, options.metadata.dict, - options.math.dim, - options.embedinfos, - options.citestyle.maybe(citeNumeric,id), - options.sanitize,options.bench,options.verbose, - options.math.mode.isStatic, - options.highlight,options.starBold,options.sandbox,options.prettyAlign) - - val (warns0, (blocks, fcontext)) = withLog("warning") { - parseBody( - initialFormatContext(icontext,options.lineMap, options.headingBase,options.pedantic, fmt), - options.lineNo, - src, - options.metadata, options.tocDepth, - options.sectionBase,options.sectionMax) - } - // TODO dont collect warnings stringly typed. - log("stdout", warns0) - // val warns = fixWarnings(warns0) - // if (warns != "") { - // log("stdout", warns) - // } - - continue(blocks, fcontext) -} From 7f5a2bc6c25290c3b5a179ba50dd7092065cce26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 13:06:31 +0100 Subject: [PATCH 13/46] Fix whitespaces :lipstick: --- src/api.kk | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/api.kk b/src/api.kk index 71e6ca0c..0cf6a244 100644 --- a/src/api.kk +++ b/src/api.kk @@ -185,32 +185,32 @@ function toBlockInfos(bs: list, inputName: string): div vector // TODO also include range information in extracted block structure function toBlockInfo(b: block, inputName: string): div maybe { match(b) { - HLine( attrs ) -> + HLine( attrs ) -> Just(BlockInfo("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Para( text, attrs ) -> + Para( text, attrs ) -> Just(BlockInfo("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Code( text, _, attrs) -> + Code( text, _, attrs) -> Just(BlockInfo("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Quote( content, attrs) -> + Quote( content, attrs) -> Just(BlockInfo("quote", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - List( _, content, attrs) -> + List( _, content, attrs) -> Just(BlockInfo("list", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Item( content, attrs) -> + Item( content, attrs) -> Just(BlockInfo("item", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Heading( _, text, attrs) -> Just(BlockInfo("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) // currently table cells are ignored - Table( _, _, _, attrs) -> + Table( _, _, _, attrs) -> Just(BlockInfo("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) Div( content, attrs) -> Just(BlockInfo("div", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Source( text, _, attrs) -> + Source( text, _, attrs) -> Just(BlockInfo("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) // DefLink( id, link) -> // DefFootnote( id, content) -> - // Empty() -> - // Special( name, value, attrs) -> - // Line( text, _, attrs) -> + // Empty() -> + // Special( name, value, attrs) -> + // Line( text, _, attrs) -> _ -> Nothing } } From 55c53d484df918aeb2dd1e9d7a046ab67da0f5a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 13:09:47 +0100 Subject: [PATCH 14/46] Revert dependency changes --- src/driver.kk | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/driver.kk b/src/driver.kk index 7b3d9dd5..58ad74eb 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -27,8 +27,6 @@ import mathStatic import texParser import includes import bibliography -import formatBlock // just the formatContext -import definitions // for parseBody public struct runners( runPdfLatex : ( srcFile : string, texFile : string, opts : options, content : string, continue : (int) -> io () ) -> io (), From b5d21a29947f27d63c42cac1c8a6301f97e66ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 16:29:40 +0100 Subject: [PATCH 15/46] Simplify api.processContent --- src/api.kk | 88 ++++++++++++++++++++++-------------------------------- 1 file changed, 36 insertions(+), 52 deletions(-) diff --git a/src/api.kk b/src/api.kk index 0cf6a244..4e667cdf 100644 --- a/src/api.kk +++ b/src/api.kk @@ -95,64 +95,48 @@ function processContent( inName : string, content : string, continue : (list, formatContext) -> io () ) : io () { - // is this ever used? - val outName = "/tmp/madoko/out.mdk" - val searchDirs = [inName.dirname] - - val opts0 = Options( + val initOptions = Options( version=version/version, lineNoWeb=True, - math=Mathoptions(mode = Dynamic), // don't generate math latex + math=Mathoptions(mode = Static), embedLimit=0, verbose=1, - copyStyles=False) - - // running fastMode destroys positioning - val fastMode = False - - content.include(fastMode, inName, outName, searchDirs, opts0) fun(icontent0,lmap) { - // remove madoko comments - val icontent = icontent0 //.removeMadokoComments - - val opts1 = opts0(lineMap=lmap, - processTimeout=if (opts0.sandbox) then 60000 else opts0.processTimeout, - metadata=opts0.metadata) - - // Just extract metadata and symbols (copied from markdownNormal) - // first normalize the input: all tabs to 4 spaces. - val (options, src) = parseMeta(opts1, FmtHtml, icontent.normalizeSource) - - val icontext = inlineContext(FmtHtml, - options.metadata.dict, - options.math.dim, - options.embedinfos, - options.citestyle.maybe(citeNumeric, id), - options.sanitize, - options.bench, - options.verbose, - options.math.mode.isStatic, - options.highlight, - options.starBold, - options.sandbox, - options.prettyAlign) - - val (warns0, (blocks, fcontext)) = withLog("warning") { - parseBody( - initialFormatContext(icontext, options.lineMap, options.headingBase, options.pedantic, FmtHtml), - options.lineNo, - src, - options.metadata, options.tocDepth, options.sectionBase, options.sectionMax) - } - // TODO don't collect warnings stringly typed. - log("stdout", warns0) - // val warns = fixWarnings(warns0) - // if (warns != "") { - // log("stdout", warns) - // } - - continue(blocks, fcontext) + copyStyles=True) + + // Just extract metadata and symbols (copied from markdownNormal) + // first normalize the input: all tabs to 4 spaces. + val (options, src) = parseMeta(initOptions, FmtHtml, content.normalizeSource) + + val icontext = inlineContext(FmtHtml, + options.metadata.dict, + options.math.dim, + options.embedinfos, + options.citestyle.maybe(citeNumeric, id), + options.sanitize, + options.bench, + options.verbose, + options.math.mode.isStatic, + options.highlight, + options.starBold, + options.sandbox, + options.prettyAlign) + + val (warns0, (blocks, fcontext)) = withLog("warning") { + parseBody( + initialFormatContext(icontext, options.lineMap, options.headingBase, options.pedantic, FmtHtml), + options.lineNo, + src, + options.metadata, options.tocDepth, options.sectionBase, options.sectionMax) } + // TODO don't collect warnings stringly typed. + log("stdout", warns0) + // val warns = fixWarnings(warns0) + // if (warns != "") { + // log("stdout", warns) + // } + + continue(blocks, fcontext) } From d56b958cb6aee078f513a1a053de93359d9e87c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 16:37:53 +0100 Subject: [PATCH 16/46] Make processContent direct style --- src/api.kk | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/api.kk b/src/api.kk index 4e667cdf..32bf2199 100644 --- a/src/api.kk +++ b/src/api.kk @@ -80,21 +80,17 @@ public function analyze( printRedirect( fun(s) { log("stdout", s) }); withLog("stdout") { - processContent(inputName, content) fun (blocks, ctx) { - val stdout = getLog("stdout") - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) - continue(res) - } + val (blocks, ctx) = processContent(content) + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) + continue(res) } () } -function processContent( - inName : string, content : string, - continue : (list, formatContext) -> io () -) : io () { +public function processContent(content : string) : io (list, formatContext) { val initOptions = Options( version=version/version, @@ -136,7 +132,7 @@ function processContent( // log("stdout", warns) // } - continue(blocks, fcontext) + (blocks, fcontext) } From de9d17436686a38df49e61a32572dca24823891b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= Date: Fri, 8 Mar 2019 16:49:19 +0100 Subject: [PATCH 17/46] Change signature of markdown to reuse it in API --- src/api.kk | 60 +++++++++++---------------------------------------- src/driver.kk | 2 +- src/madoko.kk | 8 +++---- 3 files changed, 17 insertions(+), 53 deletions(-) diff --git a/src/api.kk b/src/api.kk index 32bf2199..adba0ce9 100644 --- a/src/api.kk +++ b/src/api.kk @@ -79,8 +79,19 @@ public function analyze( ) : io () { printRedirect( fun(s) { log("stdout", s) }); + val options = Options( + version=version/version, + lineNoWeb=True, + math=Mathoptions(mode = Static), + embedLimit=0, + verbose=1, + full=Just(False), + copyStyles=False) + withLog("stdout") { - val (blocks, ctx) = processContent(content) + // TODO speed up again, by adding option to not run `formatBlocks` + // AT ALL for the presentation compiler. + val (_,_,blocks, ctx) = markdown(content, options) val stdout = getLog("stdout") val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) @@ -89,53 +100,6 @@ public function analyze( () } - -public function processContent(content : string) : io (list, formatContext) { - - val initOptions = Options( - version=version/version, - lineNoWeb=True, - math=Mathoptions(mode = Static), - embedLimit=0, - verbose=1, - copyStyles=True) - - // Just extract metadata and symbols (copied from markdownNormal) - // first normalize the input: all tabs to 4 spaces. - val (options, src) = parseMeta(initOptions, FmtHtml, content.normalizeSource) - - val icontext = inlineContext(FmtHtml, - options.metadata.dict, - options.math.dim, - options.embedinfos, - options.citestyle.maybe(citeNumeric, id), - options.sanitize, - options.bench, - options.verbose, - options.math.mode.isStatic, - options.highlight, - options.starBold, - options.sandbox, - options.prettyAlign) - - val (warns0, (blocks, fcontext)) = withLog("warning") { - parseBody( - initialFormatContext(icontext, options.lineMap, options.headingBase, options.pedantic, FmtHtml), - options.lineNo, - src, - options.metadata, options.tocDepth, options.sectionBase, options.sectionMax) - } - // TODO don't collect warnings stringly typed. - log("stdout", warns0) - // val warns = fixWarnings(warns0) - // if (warns != "") { - // log("stdout", warns) - // } - - (blocks, fcontext) -} - - // Computing position and range information // --------------------------------------- diff --git a/src/driver.kk b/src/driver.kk index 58ad74eb..789238a4 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -197,7 +197,7 @@ function process( inName : string, outName : string, searchDirs : list, function phaseHtml() { // markdown to html - val (fileEmbed,(mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions)))))) = + val (fileEmbed,(mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions,_,_)))))) = withLog("embed") { withLogCompress("math-plain") { withLogCompress("math-full") { diff --git a/src/madoko.kk b/src/madoko.kk index a359f0ae..33e37e7d 100644 --- a/src/madoko.kk +++ b/src/madoko.kk @@ -34,13 +34,13 @@ import std/log public function markdown( src : string, options : options = initialOptions0, fmt : formatter = FmtHtml - ) : pure (string,options) + ) : pure (string,options,list,formatContext) { if (options.xmp) then markdownXmp(src,options,fmt) else markdownNormal(src,options,fmt) } // Process only markdown between tags -function markdownXmp( src : string, options : options = initialOptions0, fmt : formatter = FmtHtml ) : pure (string,options) +function markdownXmp( src : string, options : options = initialOptions0, fmt : formatter = FmtHtml ) : pure (string,options,list<block>,formatContext) { val commented = "~begin htmlraw\n" + src.replaceAll(rxxmp,"\n~end htmlraw\n$2\n~begin htmlraw\n") + "\n~end htmlraw" markdownNormal(commented, options, fmt) @@ -53,7 +53,7 @@ public function normalizeSource( src : string ) : string { } // Takes source markdown input and returns formatted html -function markdownNormal( src0 : string, options0 : options = initialOptions0, fmt : formatter = FmtHtml ) : pure (string,options) +function markdownNormal( src0 : string, options0 : options = initialOptions0, fmt : formatter = FmtHtml ) : pure (string,options,list<block>,formatContext) { // first normalize the input: all tabs to 4 spaces. val (options1,src) = parseMeta(options0,fmt,src0.normalizeSource) @@ -127,7 +127,7 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm if (!xfull || !(fmt.isFmtTex)) then fmtLatexFull("",options,fcontext.inlineContext.metadata) else "" if (options.verbose>=3) then trace("done") - (res,options) + (res,options,fblocks,fcontext) } function labelPosition(label : label) : maybe<(string, int)> { From a23c8bf873c606fa9dc1472b01ed99c51aebd45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= <jonathan@b-studios.de> Date: Fri, 8 Mar 2019 18:45:52 +0100 Subject: [PATCH 18/46] Write a few more aux files for static math --- src/driver.kk | 24 +++++++++++++++++------- src/formatBlock.kk | 5 +++-- src/formatInline.kk | 15 +++++++++++++-- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/driver.kk b/src/driver.kk index 789238a4..535d0130 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -77,6 +77,8 @@ public function processContent( inName : string, outName : string, content : str // always read dims: even in dynamic mode some pdf math may exist val dims = if (mmopts.rebuild) then "" else outName.changeExt(".dimx").readTextFileDef("",False) + // parseMathDim also generates the svgdefs which are included at the bottom + // of the generated HTML page val (mdim,svgdefs,_) = dims.parseMathDim(mmopts.math) val xopts = opts2(math=(opts2.math)(dim=mdim,svgDefs=svgdefs)) @@ -197,14 +199,19 @@ function process( inName : string, outName : string, searchDirs : list<string>, function phaseHtml() { // markdown to html - val (fileEmbed,(mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions,_,_)))))) = + val (fileEmbed,(mathLocations, (mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions,_,_))))))) = withLog("embed") { - withLogCompress("math-plain") { - withLogCompress("math-full") { - withLogCompress("warning") { - withLogCompress("aux") { - markdown(icontent,xopts0) - }}}}} + withLogCompress("math-locations") { + withLogCompress("math-plain") { + withLogCompress("math-full") { + withLogCompress("warning") { + withLogCompress("aux") { + markdown(icontent,xopts0) + }}}}}} + + // write math locations to file + tryWriteTextFile(outName.appendStem("-math-locations").changeExt(".csv"), mathLocations) + // write an aux file val bibChanged = if (logs == "" || !logs.contains("\\citation{")) then [] @@ -285,6 +292,9 @@ function process( inName : string, outName : string, searchDirs : list<string>, Just((mdim2,svgdefs)) -> { // write html again mopts.print("re-aligning math in HTML.") + + mopts.print("Writing out math definitions to svg file") + tryWriteTextFile(outName.changeExt(".svg"), svgdefs) val html2 = markdown(icontent,xopts(math=(xopts.math)(dim=mdim2,svgDefs=svgdefs))).fst if (!(tryWriteTextFile(outName,html2))) { diff --git a/src/formatBlock.kk b/src/formatBlock.kk index 95431615..f7a608b7 100644 --- a/src/formatBlock.kk +++ b/src/formatBlock.kk @@ -301,8 +301,9 @@ public function formatBlock( context : formatContext, block : block ) : div stri context.fmtTable(Nil,[Row(cols)],cols.map(fun(c){ c.cellAttrs }),attrs) } - Div(content,attrs) -> { + Div(content,attrs) -> { val txt = formatBlocksX(context,content) + // trace(txt) val rnd = attrs.input match (attrs.hasKey("bib-id")) { Just(bibid) -> { @@ -343,7 +344,7 @@ function deriveMathAttrs( parentAttrs : attrs ) { attrsNone.addClasses( parentAttrs.classes.filter(fun(c) { c.startsWith("snippet") || c.startsWith("math") } ) ) .addKeyvals( parentAttrs.keyvals.filter( fun(kv) { val key = kv.fst - key.startsWith("color") || key.startsWith("font-") || key.startsWith("snippet-") || key.startsWith("math-") + key.startsWith("color") || key.startsWith("font-") || key.startsWith("snippet-") || key.startsWith("math-") || key.startsWith("data-line-") })) } diff --git a/src/formatInline.kk b/src/formatInline.kk index 4179afef..46a7d94d 100644 --- a/src/formatInline.kk +++ b/src/formatInline.kk @@ -163,10 +163,14 @@ public function fmtMath( context : inlineContext, isDisplay : bool, txt0 : strin val mcmd = context.fmtCmd("span",context.fmtText(mtxt),attrsd) if (context.mathStatic || mode.isFull /* never use mathjax for full math */) { val digest = md5(txt) + // This is where we have both the range and the md5-digest! + logMathLocation(digest, attrs) + log("math-" + mode.show, @"\begin{md" + kind + "Snippet}[" + digest + "]%mdk\n" + texcmd + "\\end{md" + kind + "Snippet}%mdk") - //trace("math: " + digest) + // trace(txt) + // trace("math: " + digest) match(context.mathinfos[digest]) { Nothing -> context.fmtCmd("span",mcmd,attrsNone.addClass("math-rendering").addKeyval("html-title","Rendering math...")) Just(mi) -> context.fmtMathImg(mi,txt,mtxt,attrsd) @@ -177,6 +181,13 @@ public function fmtMath( context : inlineContext, isDisplay : bool, txt0 : strin } } +function logMathLocation(digest: string, attrs: attrs) { + val start = attrs.lookupKey("data-line-start", "") + val end = attrs.lookupKey("data-line-end", "") + if (start == "" || end == "") return () + else log("math-locations", start + "," + end + "," + digest) +} + public function fmtMathImg( context : inlineContext, mi : mathinfo, txt : string, mtxt : string, attrs : attrs ) { val scale = match(attrs.hasKey("math-scale")) { Just(value) -> value.parseInt.maybe(100,id).double / 100.0 @@ -975,4 +986,4 @@ function insert( x : a, xs : list<a>, gt : (a,a) -> bool ) : list<a> { Cons(y,yy) -> if (gt(x,y)) then Cons(y,insert(x,yy,gt)) else Cons(x,xs) Nil -> [x] } -} \ No newline at end of file +} From e79590eb1f86de03e80fa201da4660c2d567b74b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= <jonathan@b-studios.de> Date: Fri, 8 Mar 2019 20:25:50 +0100 Subject: [PATCH 19/46] Perform includes before running markdown --- src/api.kk | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/api.kk b/src/api.kk index adba0ce9..33aa1812 100644 --- a/src/api.kk +++ b/src/api.kk @@ -74,6 +74,7 @@ public struct blockInfo ( // // TODO Currently the logs are very sparse... Did I omit too much of the // processing to catch errors? +// TODO add option to skip includes public function analyze( inputName : string, content : string, continue : (documentInfo) -> io () ) : io () { @@ -87,17 +88,19 @@ public function analyze( verbose=1, full=Just(False), copyStyles=False) - - withLog("stdout") { - // TODO speed up again, by adding option to not run `formatBlocks` - // AT ALL for the presentation compiler. - val (_,_,blocks, ctx) = markdown(content, options) - val stdout = getLog("stdout") - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) - continue(res) + + content.include(False, inputName, "out/test.mdk", [inputName.dirname], options) fun(includedContent, lmap) { + + withLog("stdout") { + // TODO speed up again, by adding option to not run `formatBlocks` + // AT ALL for the presentation compiler. + val (_,_,blocks, ctx) = markdown(includedContent, options(lineMap=lmap)) + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) + continue(res) + } } - () } // Computing position and range information From 58f30f4a5af828d414dc774983a9df18381b0aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= <jonathan@b-studios.de> Date: Fri, 8 Mar 2019 20:30:14 +0100 Subject: [PATCH 20/46] Allow skipping of includes --- madoko.d.ts | 1 + src/api.kk | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/madoko.d.ts b/madoko.d.ts index 0b610968..99d15912 100644 --- a/madoko.d.ts +++ b/madoko.d.ts @@ -33,5 +33,6 @@ declare module 'madoko' { export function analyze( inputName: string, content: string, + resolveIncludes: boolean, // perform includes? callback: (DocumentInfo) => any): any; } diff --git a/src/api.kk b/src/api.kk index 33aa1812..52ccd346 100644 --- a/src/api.kk +++ b/src/api.kk @@ -74,9 +74,8 @@ public struct blockInfo ( // // TODO Currently the logs are very sparse... Did I omit too much of the // processing to catch errors? -// TODO add option to skip includes public function analyze( - inputName : string, content : string, continue : (documentInfo) -> io () + inputName : string, content : string, resolveIncludes: bool, continue : (documentInfo) -> io () ) : io () { printRedirect( fun(s) { log("stdout", s) }); @@ -89,7 +88,7 @@ public function analyze( full=Just(False), copyStyles=False) - content.include(False, inputName, "out/test.mdk", [inputName.dirname], options) fun(includedContent, lmap) { + content.include(!resolveIncludes, inputName, "out/test.mdk", [inputName.dirname], options) fun(includedContent, lmap) { withLog("stdout") { // TODO speed up again, by adding option to not run `formatBlocks` @@ -101,6 +100,7 @@ public function analyze( continue(res) } } + () } // Computing position and range information From 0a116d192df0179de0c3e9226bac148751e78617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= <jonathan@b-studios.de> Date: Fri, 8 Mar 2019 20:46:33 +0100 Subject: [PATCH 21/46] Merge LSP api with old public api for backwards compatability --- package.json | 2 +- src/api.kk | 181 ----------------------------------------- src/madoko.kk | 219 +++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 189 insertions(+), 213 deletions(-) delete mode 100644 src/api.kk diff --git a/package.json b/package.json index 3225b6a5..8b2ef174 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "Academic" ], "preferGlobal": true, - "main": "./lib/api.js", + "main": "./lib/madoko.js", "bin": { "madoko": "./lib/cli.js" }, diff --git a/src/api.kk b/src/api.kk deleted file mode 100644 index 52ccd346..00000000 --- a/src/api.kk +++ /dev/null @@ -1,181 +0,0 @@ -module api - -import std/dict -import std/regex -import std/log -import std/path - -import backports -import version - -import block // just for the type block -import formatBlock // just for formatContext -import common // for label, lineMap, and attrs -import inline // for parseLineInfo -import driver // for outputName, processContentLSP -import metadata // for parseMeta -import madoko // for normalizeSource, lastPathSegment -import definitions // for parseBody -import options -import includes - - -// Types that are part of the public API -// ------------------------------------- - -// TODO also migrate to range -public struct position (path: string, line: int) - -// Line information is 1-based and inclusive. -public struct range (path: string, from: int, to: int) - -public struct referenceInfo ( - id: string, - element: string, - caption: string, - position: null<position> -) - -public struct documentInfo ( - labels: vector<referenceInfo>, - blocks: vector<blockInfo>, - // just for debugging purposes we also include the original formatContext - context : formatContext, - log: string -) - -// the type of block nodes we export to js / typescript -public struct blockInfo ( - // the block kind - kind: string, - // the element id, empty if not present - id: string, - // the tag name, empty if not present - name: string, - // the child nodes - content: vector<blockInfo>, - // the text content - text: string, - // the classes - classes: vector<string>, - // attributes of the block node (like "caption") - attributes: dict<string>, - // TODO also include position and range information. - position: null<range> -) - -// Main Entrypoint -// --------------- - -// Runs the frontend to parse and analyze the document but does not -// generate an html or tex document. -// -// Resolves includes and also processes those files. -// -// TODO Currently the logs are very sparse... Did I omit too much of the -// processing to catch errors? -public function analyze( - inputName : string, content : string, resolveIncludes: bool, continue : (documentInfo) -> io () -) : io () { - printRedirect( fun(s) { log("stdout", s) }); - - val options = Options( - version=version/version, - lineNoWeb=True, - math=Mathoptions(mode = Static), - embedLimit=0, - verbose=1, - full=Just(False), - copyStyles=False) - - content.include(!resolveIncludes, inputName, "out/test.mdk", [inputName.dirname], options) fun(includedContent, lmap) { - - withLog("stdout") { - // TODO speed up again, by adding option to not run `formatBlocks` - // AT ALL for the presentation compiler. - val (_,_,blocks, ctx) = markdown(includedContent, options(lineMap=lmap)) - val stdout = getLog("stdout") - val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) - val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) - continue(res) - } - } - () -} - -// Computing position and range information -// --------------------------------------- - -function range(attr: attrs, inputName: string): maybe<range> { - match (attr.hasKey("data-line-start")) { - Just(start) -> catch({ - val end = attr.lookupKey("data-line-end", start) - val (path, startLine) = start.extractPosition(inputName) - val (_, endLine) = end.extractPosition(inputName) - Just(Range(path, startLine, endLine)) - }, fun(exn) { Nothing }) - Nothing -> Nothing - } -} - -function extractPosition(locationString: string, inputName: string): exn (string, int) { - val locs = (inputName + ":" + locationString).split(";").list - val last = locs.last.split(":") - (last[0], last[1].parseInt.unJust) -} - -// we need the inputName to normalize position information -function toBlockInfos(bs: list<block>, inputName: string): div vector<blockInfo> { - vector(bs.map(fun(b){ b.toBlockInfo(inputName) }).concatMaybe) -} - -// TODO also include range information in extracted block structure -function toBlockInfo(b: block, inputName: string): div maybe<blockInfo> { - match(b) { - HLine( attrs ) -> - Just(BlockInfo("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Para( text, attrs ) -> - Just(BlockInfo("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Code( text, _, attrs) -> - Just(BlockInfo("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Quote( content, attrs) -> - Just(BlockInfo("quote", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - List( _, content, attrs) -> - Just(BlockInfo("list", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Item( content, attrs) -> - Just(BlockInfo("item", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Heading( _, text, attrs) -> - Just(BlockInfo("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - // currently table cells are ignored - Table( _, _, _, attrs) -> - Just(BlockInfo("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Div( content, attrs) -> - Just(BlockInfo("div", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - Source( text, _, attrs) -> - Just(BlockInfo("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) - // DefLink( id, link) -> - // DefFootnote( id, content) -> - // Empty() -> - // Special( name, value, attrs) -> - // Line( text, _, attrs) -> - _ -> Nothing - } -} - - -function labelInfo(elem: (string, label)): referenceInfo { - val (id, label) = elem - ReferenceInfo(id, label.element, label.labelCaption, null(label.labelPosition)) -} - -// TODO change to labelRange -function labelPosition(label : label) : maybe<position> { - match(label.labelAttrs.hasKey("data-line")) { // || label.labelAttrs.hasKey("data-line-first") - Just(info) -> { - val (path, lineno) = parseLineInfo(info) - Just(Position(path.lastPathSegment, lineno)) - // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) - } - Nothing -> Nothing - } -} diff --git a/src/madoko.kk b/src/madoko.kk index 33e37e7d..ae9bb82c 100644 --- a/src/madoko.kk +++ b/src/madoko.kk @@ -11,12 +11,18 @@ module madoko import std/dict import std/regex -import common +import std/log +import std/path + +import backports +import version + +import common // for label, lineMap, and attrs import options -import metadata -import block -import formatBlock -import inline +import metadata // for parseMeta +import block // just for the type block +import formatBlock // just for formatContext +import inline // for parseLineInfo import formatInline import definitions import entity // logEntities @@ -24,7 +30,53 @@ import htmlFormatter // for full import texFormatter // support legacy import latexFormatter import attributes // expandKeys -import std/log +import includes + + +/* -------------------------------------- + Types that are part of the public API +---------------------------------------- */ + +// TODO also migrate to range +public struct position (path: string, line: int) + +// Line information is 1-based and inclusive. +public struct range (path: string, from: int, to: int) + +public struct referenceInfo ( + id: string, + element: string, + caption: string, + position: null<position> +) + +public struct documentInfo ( + labels: vector<referenceInfo>, + blocks: vector<blockInfo>, + // just for debugging purposes we also include the original formatContext + context : formatContext, + log: string +) + +// the type of block nodes we export to js / typescript +public struct blockInfo ( + // the block kind + kind: string, + // the element id, empty if not present + id: string, + // the tag name, empty if not present + name: string, + // the child nodes + content: vector<blockInfo>, + // the text content + text: string, + // the classes + classes: vector<string>, + // attributes of the block node (like "caption") + attributes: dict<string>, + // TODO also include position and range information. + position: null<range> +) /* -------------------------------------- Main library entry point @@ -39,6 +91,47 @@ public function markdown( src : string, if (options.xmp) then markdownXmp(src,options,fmt) else markdownNormal(src,options,fmt) } +/* -------------------------------------- + Alternative entry point used by LSP server implementation +---------------------------------------- */ + +// Runs the frontend to parse and analyze the document but does not +// generate an html or tex document. +// +// Resolves includes and also processes those files. +// +// TODO Currently the logs are very sparse... Did I omit too much of the +// processing to catch errors? +public function analyze( + inputName : string, content : string, resolveIncludes: bool, continue : (documentInfo) -> io () +) : io () { + printRedirect( fun(s) { log("stdout", s) }); + + val options = Options( + version=version/version, + lineNoWeb=True, + math=Mathoptions(mode = Static), + embedLimit=0, + verbose=1, + full=Just(False), + copyStyles=False) + + content.include(!resolveIncludes, inputName, "out/test.mdk", [inputName.dirname], options) fun(includedContent, lmap) { + + withLog("stdout") { + // TODO speed up again, by adding option to not run `formatBlocks` + // AT ALL for the presentation compiler. + val (_,_,blocks, ctx) = markdown(includedContent, options(lineMap=lmap)) + val stdout = getLog("stdout") + val labeledElems = ctx.inlineContext.labels.list().map(labelInfo) + val res = DocumentInfo(vector(labeledElems), blocks.toBlockInfos(inputName), ctx, stdout) + continue(res) + } + } + () +} + + // Process only markdown between <xmp> tags function markdownXmp( src : string, options : options = initialOptions0, fmt : formatter = FmtHtml ) : pure (string,options,list<block>,formatContext) { @@ -99,15 +192,11 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm if (fmt.isFmtHtml) { fcontext.inlineContext.labels.list().foreach fun(elem) { val (name,label) = elem - val labelName = "\"name\": " + name.json - val labelPos = match(label.labelPosition) { - Just((path, lineno)) -> ", \"position\": {\"path\": \"" + path + "\", \"line\":" + lineno.show + "}" - Nothing -> "" - } + val labelName = "\"name\": " + name.json val element = ", \"element\": " + label.element.json val text = ", \"text\": " + label.labelText.json val caption = ", \"caption\": " + label.labelCaption.json - log("labels","{" + labelName + labelPos + element + text + caption + " }"); + log("labels","{" + labelName + element + text + caption + " }"); } fcontext.inlineContext.links.list().foreach fun(elem) { val (name,link) = elem @@ -130,25 +219,6 @@ function markdownNormal( src0 : string, options0 : options = initialOptions0, fm (res,options,fblocks,fcontext) } -function labelPosition(label : label) : maybe<(string, int)> { - match(label.labelAttrs.hasKey("data-line")) { - Just(info) -> { - val (path, lineno) = parseLineInfo(info) - Just((path.lastPathSegment, lineno)) - // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) - } - Nothing -> Nothing - } -} - -public function lastPathSegment( lineInfo : string ) : string { - match (lineInfo.find(rxLastPath)) { - Nothing -> lineInfo - Just(cap) -> cap.groups[1] - } -} -val rxLastPath = regex( @";([^:;]+):$") - // Export initial options for JavaScript usage public val initialOptions0 = initialOptions(); @@ -216,3 +286,90 @@ public function inlineContext( fmt : formatter, fmt ) } + + +/* -------------------------------------- + Conversions between internal and external datatypes +---------------------------------------- */ + +function range(attr: attrs, inputName: string): maybe<range> { + match (attr.hasKey("data-line-start")) { + Just(start) -> catch({ + val end = attr.lookupKey("data-line-end", start) + val (path, startLine) = start.extractPosition(inputName) + val (_, endLine) = end.extractPosition(inputName) + Just(Range(path, startLine, endLine)) + }, fun(exn) { Nothing }) + Nothing -> Nothing + } +} + +function extractPosition(locationString: string, inputName: string): exn (string, int) { + val locs = (inputName + ":" + locationString).split(";").list + val last = locs.last.split(":") + (last[0], last[1].parseInt.unJust) +} + +// we need the inputName to normalize position information +function toBlockInfos(bs: list<block>, inputName: string): div vector<blockInfo> { + vector(bs.map(fun(b){ b.toBlockInfo(inputName) }).concatMaybe) +} + +// TODO also include range information in extracted block structure +function toBlockInfo(b: block, inputName: string): div maybe<blockInfo> { + match(b) { + HLine( attrs ) -> + Just(BlockInfo("hline", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Para( text, attrs ) -> + Just(BlockInfo("para", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Code( text, _, attrs) -> + Just(BlockInfo("code", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Quote( content, attrs) -> + Just(BlockInfo("quote", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + List( _, content, attrs) -> + Just(BlockInfo("list", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Item( content, attrs) -> + Just(BlockInfo("item", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Heading( _, text, attrs) -> + Just(BlockInfo("heading", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + // currently table cells are ignored + Table( _, _, _, attrs) -> + Just(BlockInfo("table", attrs.name, attrs.elem, vector(), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Div( content, attrs) -> + Just(BlockInfo("div", attrs.name, attrs.elem, content.toBlockInfos(inputName), "", vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + Source( text, _, attrs) -> + Just(BlockInfo("source", attrs.name, attrs.elem, vector(), text, vector(attrs.classes), dict(attrs.keyvals), attrs.range(inputName).null)) + // DefLink( id, link) -> + // DefFootnote( id, content) -> + // Empty() -> + // Special( name, value, attrs) -> + // Line( text, _, attrs) -> + _ -> Nothing + } +} + + +function labelInfo(elem: (string, label)): referenceInfo { + val (id, label) = elem + ReferenceInfo(id, label.element, label.labelCaption, null(label.labelPosition)) +} + +// TODO change to labelRange +function labelPosition(label : label) : maybe<position> { + match(label.labelAttrs.hasKey("data-line")) { // || label.labelAttrs.hasKey("data-line-first") + Just(info) -> { + val (path, lineno) = parseLineInfo(info) + Just(Position(path.lastPathSegment, lineno)) + // if (lineno <= 0 || info.startsWith("0;")) then Nothing else Just((path.lastPathSegment, lineno)) + } + Nothing -> Nothing + } +} + +public function lastPathSegment( lineInfo : string ) : string { + match (lineInfo.find(rxLastPath)) { + Nothing -> lineInfo + Just(cap) -> cap.groups[1] + } +} +val rxLastPath = regex( @";([^:;]+):$") From efc9de11d9f791f1015e731202b759943749d850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Brachtha=CC=88user?= <jonathan@b-studios.de> Date: Thu, 14 Mar 2019 15:39:09 +0100 Subject: [PATCH 22/46] Always provide second argument to avoid problems with trailing brackets --- src/latexFormatter.kk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/latexFormatter.kk b/src/latexFormatter.kk index c54c79e8..f5a79d78 100644 --- a/src/latexFormatter.kk +++ b/src/latexFormatter.kk @@ -280,7 +280,7 @@ function fmtLatexPolyRow(cells: list<ccell>, col: int) : string { match(cells) { Nil -> "" Cons(cell, rest) -> { - val rendered = "\\>[C" + col.show + "]" + cell.content + val rendered = "\\>[C" + col.show + "][l]" + cell.content rendered + fmtLatexPolyRow(rest, col + cell.span) } } From 1311ab6a84db5f82fa0cf0734942e0d98ffb6292 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Thu, 21 Nov 2019 12:46:17 -0800 Subject: [PATCH 23/46] fix doi url handling; add acmart bib style support (showISSN etc) --- package.json | 50 ++++++++-------- src/texParser.kk | 31 +++++----- src/texParserItems.kk | 130 ++++++++++++++++++++++-------------------- 3 files changed, 109 insertions(+), 102 deletions(-) diff --git a/package.json b/package.json index ac159b7c..a204afd5 100644 --- a/package.json +++ b/package.json @@ -1,21 +1,23 @@ { - "name" : "madoko", - "author" : "Daan Leijen, Microsoft Corp.", - "version" : "1.1.6", - "homepage" : "http://madoko.codeplex.com", - "description" : "Madoko is a fast scholarly Markdown processor written in Koka", - "licenses": [{ - "type" : "Apache License 2.0", - "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" - }], + "name": "madoko", + "author": "Daan Leijen, Microsoft Corp.", + "version": "1.1.6", + "homepage": "http://madoko.codeplex.com", + "description": "Madoko is a fast scholarly Markdown processor written in Koka", + "licenses": [ + { + "type": "Apache License 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], "keywords": [ - "Madoko", - "Markdown", - "LaTeX", - "Koka", - "Microsoft", - "Scholarly", - "Academic" + "Madoko", + "Markdown", + "LaTeX", + "Koka", + "Microsoft", + "Scholarly", + "Academic" ], "preferGlobal": true, "main": "./lib/madoko.js", @@ -33,19 +35,19 @@ "readme.md", "license.txt" ], - "engines" : { - "node": ">=0.10.0" + "engines": { + "node": ">=0.10.0" }, "dependencies": { - "amdefine" : ">=0.0.4", - "requirejs" : ">=2.1", - "mkdirp" : ">=0.3.5" + "amdefine": ">=0.0.4", + "requirejs": ">=2.1", + "mkdirp": ">=0.3.5" }, "devDependencies": { - "jake" : ">=0.7.6" + "jake": ">=0.7.6" }, "repository": { - "type": "hg", - "url" : "https://hg.codeplex.com/madoko" + "type": "hg", + "url": "https://hg.codeplex.com/madoko" } } diff --git a/src/texParser.kk b/src/texParser.kk index ca85aaad..fbe3aa0b 100644 --- a/src/texParser.kk +++ b/src/texParser.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -25,7 +25,7 @@ import texParserBase import texParserItems /* -------------------------------------- - Latex grammar + Latex grammar ---------------------------------------- */ @@ -68,7 +68,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texcomment1", regex(@"^\\begin{comment}([\s\S]*?)\\end{comment}"), texComment ), Rule("texcomment2", regex(@"^(\n *%.*\n(?: *%.*\n)*)"), texComment), - Rule("texlcomment", regex(@"^(%.*)"), texComment), + Rule("texlcomment", regex(@"^(%.*)"), texComment), Rule("texfigure", regex(@"^\\begin{(figure|table)(\*)?}" + optarg + @"?([\s\S]*?)\\end{\1\*?}"), texCaptioned ), Rule("texwfigure", regex(@"^\\begin{wrapfigure}" + optarg + texarg + texarg + @"?([\s\S]*?)\\end{wrapfigure}"), texWrapFigure ), Rule("texlabeled", regex(@"^\\begin{(definition|lemma|theorem|proposition|corollary|proof|example|thm|dfn)(\*)?}" + optarg + @"?([\s\S]*?)\\end{\1\*?}"), texLabeled ), @@ -84,7 +84,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texnoindent", regex(@"^\\noindent\b([\s\S]*?\n) *\n"), texNoIndent ), Rule("texvspacex", regex(@"^\\vspace\b\*?\s*" + texarg + @"(?=(?:\s|%.*)*[^\\\{\s])([\s\S]*?\n) *(?=\n|\\(?:end|item|begin|noindent)\b)"), texVSpacePara ), - + Rule("texminipage", regex(@"^\\begin{minipage}\s*" + optarg + "?" + optarg + "?" + optarg + "?" + texarg ), texBeginMinipage ), Rule("texvspace", regex(@"^\\vspace\b\*?\s*" + texarg), texVSpace ), Rule("texhspace", regex(@"^\\hspace\b\*?\s*" + texarg), texHSpace ), @@ -102,7 +102,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texonly", regex(@"^\\(uncover|only|visible)\b\s*" + overlay + "?" + texargg), texUncover ), Rule("texbcblock", regex(@"^\\begin{(" + texenvcaption + @")}\s*" + overlay + "?" + texargg), texBeginCaptionBlock ), Rule("texecblock", regex(@"^\\end{(" + texenvcaption + @")}"), texEndCaptionBlock ), - + /* Specials */ Rule("texinclude", regex(@"^\\(include(?:only)?|input)\b\s*" + texarg + eol), texInclude ), Rule("textitle", regex(@"^\\(?:maketitle|titlepage)\b" + eol), texMakeTitle ), @@ -121,7 +121,7 @@ val texGrammar : grammar<string,texContext> = [ /* Common inline elements */ Rule("texref", regex(@"^(~)?\\ref\b" + texargg), texRef ), Rule("texverb", regex(@"^\\(?:verb|lstinline)\*?([!\+%\|\.\^#@])(.*?)\1"), texCode ), - + Rule("texmath1", regex(@"^(?:\$(?:[^\\\$]|\\.)+\$|\\\((?:[^\\]|\\[^\)])*\\\))"), texMathInline ), Rule("texmath2", regex(@"^(?:\$\$(?:[^\\\$]|\\.|\$(?!\$))*\$\$|\\\[(?:[^\\]|\\[^\]])*\\\])"), texMathDisplay ), Rule("texmathenv1", regex(@"^\\begin\{(equation|displaymath)(\*)?\}([\s\S]*?)\\end{\1\2}"), texMathEnv ), @@ -145,6 +145,8 @@ val texGrammar : grammar<string,texContext> = [ Rule("cite", regex(@"^\\(short)?[Cc]ite(?:(author)|(year(?:par)?)|(al)?([pt]|A?N?P?)?(\*)?)\b" + optarg + "?" + optarg + "?" + texarg), texCite), Rule("bibinfo", regex(@"^\\bibinfo\b\s*" + texarg + @"\s*" + texarg), texBibinfo ), Rule("bblname", regex(@"^\\bbl([a-z]+)\b\s*"), texBblName ), // for custom-bib international bst styles + Rule("showISSN", regex(@"^\\show(ISSN|LCCN|CODEN|DOI|ISBN(?:[a-z]*)?|note|articletitle)\b\s*" + texarg), texBibinfo ), + Rule("showDOI", regex(@"^\\urldef\\tempurl" + eol + @"\\url" + eol + texarg + eol + @"\\showDOI\{\\tempurl\}"), texDoi ), Rule("texsl", regex(@"^\{\\(?:text)?sl\b" + texargs + @"\}"), texSlanted ), Rule("texsl2", regex(@"^\\{\\slshape\b\s*" + texargs + @"\}"), texSlanted ), @@ -164,7 +166,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texsf1", regex(@"^\\textsf\b\s*" + texarg), texSans ), Rule("texsf2", regex(@"^\{\\sffamily\b\s*" + texargs + @"\}"), texSans ), Rule("texquote", regex(@"^\\enquote\b\s*" + texarg), texDquot ), - Rule("texccmd", regex(@"^\\(alert)\b\s*" + texarg), texClassCmd ), + Rule("texccmd", regex(@"^\\(alert)\b\s*" + texarg), texClassCmd ), Rule("texeol", regex(@"^\\\\" + optarg + "?"), texLineBreak ), Rule("texcolor1", regex(@"^\\(?:text|cell)color\b\s*" + optarg + "?" + texargg + texargg), texTextColor ), Rule("texcolor2", regex(@"^\{\s*\\color\b\s*" + optarg + "?" + texargg + texargs + @"\}"), texTextColor ), @@ -182,7 +184,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texdquot", regex(@"^``((?:[^'\\\n]|\\.|'(?!'))*)''"), texDquot ), Rule("texsquot", regex(@"^`((?:[^'\\\n]|\\.)*)'"), texSquot ), - Rule("texdquot1", regex(@"^``"), texOther ), + Rule("texdquot1", regex(@"^``"), texOther ), Rule("texrule", regex(@"^\\rule\b\s*" + optarg + "?" + texarg + texarg ), texRule ), @@ -197,7 +199,7 @@ val texGrammar : grammar<string,texContext> = [ Rule("texignore2", regex(@"^\\(" + texignore2 + @")\b\s*" + optargs + texargg + texargg), texAlwaysIgnore4 ), Rule("texignore1", regex(@"^\\(" + texignore1 + @")\b\s*" + optargs + texarg), texAlwaysIgnore3 ), Rule("texignore0", regex(@"^\\(" + texignore0 + @")\b\s*" + optargs), texAlwaysIgnore ), - + Rule("texenvbegin", regex(@"^\\begin\{([a-zA-Z@]+)(\*)?\}\s*" + overlay + "?" + optarg + @"?\s*"), texEnvBegin), Rule("texenvend", regex(@"^\\end\{([a-zA-Z@]+)\*?\}\s*"), texEnvEnd), @@ -212,19 +214,19 @@ val texGrammar : grammar<string,texContext> = [ Rule("texraw1", regex(@"^\\" + texRawCmd1 + @"\b\s*" + optargs + texarg), texRaw ), Rule("texraw2", regex(@"^\\" + texRawCmd2 + @"\b\s*" + optargs + texarg + texarg), texRaw ), - Rule("texgroup", regex(@"^\{" + texargs + @"\}" ), texGroup ), + Rule("texgroup", regex(@"^\{" + texargs + @"\}" ), texGroup ), Rule("texcmd2", regex(@"^\\(" + texcmd2 + @")\b\s*" + optargs + texarg + optargs + texarg), texIgnore ), Rule("texcmd1", regex(@"^\\(" + texcmd1 + @")\b\s*" + optargs + texarg), texIgnore ), //Rule("texcmd0", regex(@"^\\(" + texcmd0 + @")\b\s*" + optargs), texIgnore ), Rule("texcmd", regex(@"^(\\[a-zA-Z@]+)\b\s*(" + optargs + ")"), texGenericCommand ), Rule("texchar", regex(@"^\\[^a-zA-Z]"), texChar ), - + Rule("texother", regex(@"^[\s\S]"), texOther ), ] /* -------------------------------------- - Parse TeX elements + Parse TeX elements ---------------------------------------- */ @@ -234,7 +236,7 @@ public function parseTex( txt : string, id: string = "", citestyle : citestyle = val normTxt = txt.replaceAll(regex(@"\r\n?"),"\n").replaceAll(regex(@"\t")," ") /* // strip initial comments - .replaceAll(regex(@"^(\s|%.*)+"),"") + .replaceAll(regex(@"^(\s|%.*)+"),"") // strip off comments // little conservative but % sometimes occurs inside url's .replaceAll(rxTexComment, fun(cap) { @@ -261,6 +263,5 @@ function parseTexAcc( context : texContext, acc : builder<h>, txt : string ) : s { if (txt=="") return acc.build val (s,next,_) = matchRules(context.grammar,context,txt,id) - parseTexAcc(context, acc.append(s), txt.substr1(next)) + parseTexAcc(context, acc.append(s), txt.substr1(next)) } - diff --git a/src/texParserItems.kk b/src/texParserItems.kk index b87b6f09..912c0ff7 100644 --- a/src/texParserItems.kk +++ b/src/texParserItems.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -31,7 +31,7 @@ val texEntityCmd0 = @"(vfill|qed|eg|etal|ie|" + texEntities.keys.join("|") + ")" val texFontSizeMap = [("tiny","xx-small"),("scriptsize","x-small"),("footnotesize","small"), ("small","small"),("normalsize","medium"),("large","large"),("Large","x-large"), ("LARGE","xx-large"),("Huge","xx-large")].dict -val texFontSizes = texFontSizeMap.keys.join("|") +val texFontSizes = texFontSizeMap.keys.join("|") /* sections, sizes, etc */ val texsects = ["part","chapter","section","subsection","subsubsection","paragraph"] @@ -39,11 +39,11 @@ val texsect = "(" + texsects.join("|") + ")" /* -------------------------------------- - Latex block functions + Latex block functions ---------------------------------------- */ function texInclude( cap : matched, _context ) { - val fname = cap.groups[2].unbrace - if (cap.groups[1]=="input" && fname=="babelbst.tex") + val fname = cap.groups[2].unbrace + if (cap.groups[1]=="input" && fname=="babelbst.tex") then "" // already supported in Madoko but inserted by the custom-bib babel bib styles else "\n[INCLUDE=" + cap.groups[2].unbrace + "]\n" } @@ -107,7 +107,7 @@ function texComment( cap : matched, _context ) : string { } function texRef( cap : matched, _context ) : string { - (if (cap.groups[1]=="~") then " " else cap.groups[1]) + "[#" + cap.groups[2].toIdentifier + "]" + (if (cap.groups[1]=="~") then " " else cap.groups[1]) + "[#" + cap.groups[2].toIdentifier + "]" } @@ -125,7 +125,7 @@ function texBeginMinipage( cap : matched, _context ) : string { val height = texLength(cap.groups[2]) val attrs = [makeAttr("width",width), makeAttr("height",height)].joinAttrs - "\n~ Begin Minipage " + attrs + "\n" + "\n~ Begin Minipage " + attrs + "\n" } function texItemPause( cap : matched, context ) : string { @@ -174,7 +174,7 @@ function texMathEnv( cap : matched, context ) { function texMathSnippet( cap : matched, context ) { val env = cap.groups[1] - val lenv = + val lenv = if (env=="gather") then "gathered" elif (env=="align" || env=="multline" || env=="flalign" || env=="eqnarray") then "aligned" else "" @@ -197,9 +197,9 @@ function texTikzCmd( cap : matched, context ) { function texListing( cap : matched, context ) : string { val attrs = if (cap.groups[2]=="") then "" else { - " { " + cap.groups[2].findAll(rxKeyVal).map( fun(kcap) { + " { " + cap.groups[2].findAll(rxKeyVal).map( fun(kcap) { val key = kcap.groups[1] - val value = kcap.groups[2] + val value = kcap.groups[2] if (key=="language") then "language:'" + value + "'" elif (key=="caption") then "caption:'" + parse( value.unbrace, context ) + "'" elif (key=="label") then "#" + value.toIdentifier @@ -231,13 +231,13 @@ function texWrapFigure( cap : matched, context ) : string { if (cap.groups[2]=="r" || cap.groups[2]=="R" || cap.groups[2]=="o" || cap.groups[2]=="O") then "float:right; margin-left:1em" else "float:left; margin-right:1em" - ] + ] texCaptionedXX( "figure", cap.groups[4], "", attrs, context ) } function texCaptionedXX( env :string, body: string, caption:string, extraAttrs : list<string>, context ) : string { val (content,labelCaption,pre,post) = extractLabelCaption( body, context, caption.parse(context), "" ) - val attrs = (extraAttrs + labelCaption).joinAttrs + val attrs = (extraAttrs + labelCaption).joinAttrs "\n~ Begin " + env.capitalize + " " + attrs + "\n" + pre + content.trimNL.parse(context) + post + "\n~ End " + env.capitalize + "\n" @@ -338,15 +338,15 @@ function texTabularDo( pos :string, width: string, spec : string, content : stri } } } - + function renderRow( row : string, columns : int) : string { val (line,rest) = match(row.find(rxHline)) { Nothing -> ("",row) Just(rcap) -> (makeLine(rcap.groups[1]!="", columns), rcap.groups[3]) } - if (rest.trim == "") + if (rest.trim == "") then line - else line + (if (line=="") then "" else "\n") + + else line + (if (line=="") then "" else "\n") + "| " + rest.split("&").list.map( renderCell ).expand(columns).join("| ") + "|" } @@ -359,7 +359,7 @@ function texTabularDo( pos :string, width: string, spec : string, content : stri } function createColumns( _colspecs, row ) { val cols = row.split("&") - "\n~Begin Columns" + cols.map(createColumn).join() + "\n~ End Columns\n" + "\n~Begin Columns" + cols.map(createColumn).join() + "\n~ End Columns\n" } // Split into rows @@ -388,26 +388,26 @@ function texTabularDo( pos :string, width: string, spec : string, content : stri val colspecs = spec.replaceAll(rxNoSpec,"").findAll(rxSpec).list.toColSpecs val header = colspecs.map( fun(cspec) { val (sep,kind,attrs) = cspec - if (kind=="") - then sep + if (kind=="") + then sep else sep + (if (kind=="c" || kind=="l") then ":" else "") + headerSep + attrs + headerSep + (if (kind=="c" || kind=="r") then ":" else "") }).join(""); val colcount = (if (colspecs.length > 1) then colspecs.length-1 else 1) // use 'column' block for single line tabular's with new lines in it - if (rawRows.length==1 && rawRows.any(fun(row) { row.contains(rxRowNl) })) + if (rawRows.length==1 && rawRows.any(fun(row) { row.contains(rxRowNl) })) then return createColumns(colspecs,rawRows.take(1).join) //trace("|rawrows| = " + rawRows.length.show + "\n[" + rawRows.join(",") + "]\n\n") - + // create attributes val attrs = [".textable", makeAttr("width",width.texLength), - (if (pos=="b") then "vertical-align:bottom" - elif (pos=="t") then "vertical-align:top" + (if (pos=="b") then "vertical-align:bottom" + elif (pos=="t") then "vertical-align:top" else "")].joinAttrs - // render rows + // render rows function renderRows( rs ) { rs.map( fun(row) { renderRow( row, colcount ) } ).remove(isEmpty).join("\n") } @@ -475,14 +475,14 @@ function texFrameCmd( cap : matched, context : texContext ) : string { texFrameX( cap.groups[2], "", "", cap.groups[3], context ) } -function texFrameX( options : string, argTitle : string, argSubtitle : string, body : string, context : texContext ) : string { +function texFrameX( options : string, argTitle : string, argSubtitle : string, body : string, context : texContext ) : string { val (title0,attrs0) = options.beamerOptions val (content,title1,attrs1,pre,post) = body.beamerExtract(context) val title = argTitle.unbrace || title1 || title0 val subtitle = if (argSubtitle.isEmpty) then "" else " - " + argSubtitle.unbrace val rtitle = (title + subtitle).parse(context) - val ftitle = rtitle.trim || @"\/" - "\n# " + ftitle + " " + (attrs0 + attrs1).joinAttrs + "\n" + pre + content.parse(context) + post + "\n" + val ftitle = rtitle.trim || @"\/" + "\n# " + ftitle + " " + (attrs0 + attrs1).joinAttrs + "\n" + pre + content.parse(context) + post + "\n" } function texColumn( cap : matched, context ) : string { @@ -497,7 +497,7 @@ function texUncover( cap : matched, context ) : string { function texBeginCaptionBlock( cap : matched, context ) : string { val attrs = ["." + cap.groups[1], if (cap.groups[2].isEmpty) then "" else ".fragment", - makeAttr("caption", cap.groups[3].parse(context)) + makeAttr("caption", cap.groups[3].parse(context)) ].joinAttrs "\n~ Begin Captioned " + attrs + "\n" } @@ -507,11 +507,11 @@ function texEndCaptionBlock( cap : matched, context ) : string { } /* -------------------------------------- - Latex grammar functions + Latex grammar functions ---------------------------------------- */ function texNormal( cap : matched, _context ) : string { - cap.matched + cap.matched } function texIndent( cap : matched, _context ) : string { @@ -534,11 +534,11 @@ function texOther( cap : matched, _context ) : string { elif (char=="*") then @"\*" elif (char=="_") then @"\_" elif (char=="[") then @"\[" - elif (char=="]") then @"\]" - elif (char=="#") then @"\#" - elif (char=="`") then @"\`" + elif (char=="]") then @"\]" + elif (char=="#") then @"\#" + elif (char=="`") then @"\`" elif (char=="``") then "\"" - else char + else char } @@ -547,7 +547,7 @@ function texAccent( cap : matched, _context ) : string { val letter = cap.groups[2].replaceAll(rxNonLetter,"") match (texAccents[cap.groups[1]]) { Just(name) -> "&" + letter + name + ";" - Nothing -> letter + Nothing -> letter } } val rxNonLetter = regex(@"[^\w]+"); @@ -675,14 +675,14 @@ function texParBox( cap : matched, context ) : string { makeAttr("text-align", texAlign(cap.groups[2])), makeAttr("height", texLength(cap.groups[3])), makeAttr("vertical-align", texVAlign(cap.groups[4])), - ].joinAttrs + ].joinAttrs "\n~ Parbox " + attrs + "\n" + cap.groups[4].parse(context).trimNL + "\n~\n" } function texRaiseBox( cap : matched, context ) : string { val attrs = [makeAttr("height", texLength(cap.groups[3])), makeAttr("vertical-align", texLength(cap.groups[2])), - ].joinAttrs + ].joinAttrs "[" + cap.groups[4].parse(context) + "]" + attrs } @@ -694,14 +694,14 @@ function texImage( cap : matched, context ) : string { if (key=="width") then "width:\"" + texLength(value) + "\"" elif (key=="height") then "height:\"" + texLength(value) + "\"" elif (key=="scale") then "transform:\"scale(" + value + ")\"" - elif (key=="angle") then "transform:\"rotate(" + value + "deg)\"" + elif (key=="angle") then "transform:\"rotate(" + value + "deg)\"" else "" }) - val kvs1 = if (cap.groups[1].isEmpty) then [] else [".fragment"] + val kvs1 = if (cap.groups[1].isEmpty) then [] else [".fragment"] val fname = cap.groups[3].unbrace val imgname = if (fname.extname=="") then fname + ".eps" else fname val imgkey = imgname.stemname.replaceAll(regex(@"[^\w\-]+"),"") - "![" + imgkey + "]" + cap.groups[4].parse(context) + "\n\n[" + imgkey + "]: " + "![" + imgkey + "]" + cap.groups[4].parse(context) + "\n\n[" + imgkey + "]: " + imgname + " \"image title\" { " + (kvs0 + kvs1).join(" ; ") + " }\n" } @@ -730,9 +730,9 @@ function texClassCmd( cap : matched, context : texContext ) : string { function parseColor( c : string ) : string { val n = c.parseIntDefault(0) - if (n < 0 || n > 255) + if (n < 0 || n > 255) then "00" - else n.showHex(2) + else n.showHex(2) } function parseFloatColor( c : string ) : string { @@ -743,11 +743,11 @@ function parseFloatColor( c : string ) : string { } function parseColors(r,g,b) : string { - "#" + parseColor(r) + parseColor(g) + parseColor(b) + "#" + parseColor(r) + parseColor(g) + parseColor(b) } function parseFloatColors(r,g,b) : string { - "#" + parseFloatColor(r) + parseFloatColor(g) + parseFloatColor(b) + "#" + parseFloatColor(r) + parseFloatColor(g) + parseFloatColor(b) } @@ -777,7 +777,7 @@ function extractColorX( model : string, value : string ) : string { } function extractColor( model : string, value : string ) : list<string> { - [makeAttr("color",extractColorX(model,value))] + [makeAttr("color",extractColorX(model,value))] } function texTextColor( cap : matched, context ) : string { @@ -845,7 +845,7 @@ function texMonospace( cap : matched, context : texContext ) : string { function texUrl( cap : matched, context : texContext ) : string { val txt = cap.groups[1].unbrace val url = txt.urlEncode - if (txt=="") then "" + if (txt=="") then "" elif (txt==url) then "<" + txt + ">" else ("[" + txt + "](" + txt.urlEncode + ")") } @@ -857,7 +857,11 @@ function texUrlPrefix( cap : matched, context : texContext ) : string { function texDoi( cap : matched, context : texContext ) : string { val txt = cap.groups[1].unbrace - if (txt=="") then "" else ("doi:[" + txt + "](http://dx.doi.org/" + urlEncode(txt) + ")") + val path = match(txt.find( regex(@"^https?://(?:dx\.)?doi\.org/(.*)$"))) { + Just(capx) -> capx.groups[1] + Nothing -> txt + } + if (txt=="") then "" else ("[doi:[" + path + "](https://doi.org/" + urlEncode(path) + ")]{.doi}") } function urlEncode(txt) : string { @@ -869,7 +873,7 @@ function urlEncode(txt) : string { val rxUrlReserved = regex(@"[\s>)\'\""(<\[\]]") function texBibinfo( cap : matched, context : texContext ) : string { - "[" + cap.groups[2].parse(context) + "]{ .bibinfo; field:\"" + cap.groups[1].unbrace + "\"}" + "[" + cap.groups[2].parse(context) + "]{ .bibinfo; field:\"" + cap.groups[1].unbrace.toLower + "\"}" } function texBblName( cap : matched, context : texContext ) : string { @@ -891,7 +895,7 @@ function texTheBibliography( cap : matched, context : texContext ) : string { "~ Begin Bibliography { " + numcite + " ; caption:" + cap.groups[1].unbrace.quote + (if (context.bststyle.bool) then " ; data-style:" + context.bststyle.quote else "") + - " }\n" + (cap.groups[2] + "\n\n").parse(context) + + " }\n" + (cap.groups[2] + "\n\n").parse(context) + "\n~ End Bibliography\n" } @@ -901,20 +905,20 @@ function texCiteAuthorYear( cap : matched, context : texContext ) : string { function texCite( cap : matched, context : texContext ) : string { // handles various natbib and chicago style citations - // groups: 1: short? 2: author? 3: year|yearpar?, 4: al?, 5: p or t?, or A?N?P? + // groups: 1: short? 2: author? 3: year|yearpar?, 4: al?, 5: p or t?, or A?N?P? // 6: *?, 7: optarg1, 8: optarg2, 9: citations - val citations = cap.groups[9].unbrace.split(rxCiteSep) - val modifier = if (cap.groups[6]=="*") then "+" + val citations = cap.groups[9].unbrace.split(rxCiteSep) + val modifier = if (cap.groups[6]=="*") then "+" elif (cap.groups[2]=="author" || cap.groups[5].contains("A")) then "!" - elif (cap.groups[3].startsWith("year") || cap.groups[1]=="short") then "-" + elif (cap.groups[3].startsWith("year") || cap.groups[1]=="short") then "-" else "" - function bracket(s) { + function bracket(s) { if (cap.groups[3]=="year" || cap.groups[2]=="author" || - cap.groups[5] == "t" || cap.groups[5].contains("N")) - then s else "[" + s + "]" + cap.groups[5] == "t" || cap.groups[5].contains("N")) + then s else "[" + s + "]" } val classes = if (cap.groups[4]=="al"||cap.groups[5]=="NP") then "{.free}" else "" - + val (pre,post) = if (cap.groups[8]!="") then (cap.groups[7] + " ", "," + cap.groups[8]) elif (cap.groups[7] != "") then ("","," + cap.groups[7]) @@ -927,9 +931,9 @@ val rxCiteSep = regex(@", *") function texHarvarditem( cap : matched, context : texContext ) : string { val key = cap.groups[4] val year = "(" + cap.groups[3] + ")" - val lab = if (cap.groups[1]!="") - then cap.groups[1] + year + cap.groups[2] - else cap.groups[2] + year + val lab = if (cap.groups[1]!="") + then cap.groups[1] + year + cap.groups[2] + else cap.groups[2] + year val content = cap.groups[5] context.makeBibitem(key,lab,content) } @@ -962,7 +966,7 @@ function texBibitem( cap : matched, context : texContext ) : string { function makeBibitem( context : texContext, keyx : string, labx : string, content : string ) : string { val key = (if (context.name.isEmpty) then "" else context.name + ":") + keyx.unbrace - val lab = labx.unbrace.parse(context) + val lab = labx.unbrace.parse(context) val md = content.parse(context) val (authors,title) = match(md.find(rxAuthorTitle)) { Just(cap) -> (cap.groups[1],cap.groups[2]) @@ -973,9 +977,9 @@ function makeBibitem( context : texContext, keyx : string, labx : string, conten .replaceAll("\n"," ").replaceAll("'","&apos;").replaceAll("\\","&bslash;") .replaceAll(rxSpaces," ").trim */ - + val search = (title + "+" + authors).replaceAll(rxNonSearch," ").replaceAll(rxSpaces,"+") - + val attrs0 = match(lab.find(rxAuthorYear)) { Nothing -> if (lab=="") then [] else [ makeAttr("cite-label", lab), @@ -1000,7 +1004,7 @@ function makeBibitem( context : texContext, keyx : string, labx : string, conten val attrs = (attrs0 + attrs1).joinAttrs "~ Begin Bibitem " + attrs + "\n" + - md + + md + "\n~ End Bibitem\n" } From a632ee10ecbc384ddcd8a300165d7dfc54792870 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Wed, 5 Feb 2020 17:20:58 -0800 Subject: [PATCH 24/46] update madoko-local for node 12+ --- support/madoko-local/package.json | 16 ++++----- support/madoko-local/readme.md | 38 ++++++++++----------- support/madoko-local/src/init.js | 18 +++++----- support/madoko-local/src/util.js | 50 ++++++++++++++++------------ support/madoko-local/versionlog.json | 7 ++++ 5 files changed, 71 insertions(+), 58 deletions(-) diff --git a/support/madoko-local/package.json b/support/madoko-local/package.json index 6ffcf07d..74c67895 100644 --- a/support/madoko-local/package.json +++ b/support/madoko-local/package.json @@ -1,18 +1,18 @@ { "name" : "madoko-local", "author" : "Daan Leijen, Microsoft Research", - "version" : "0.9.0", + "version" : "0.9.1", "homepage" : "http://madoko.codeplex.com", "description" : "Madoko-Local provides local disk access to Madoko.net", "licenses": [{ - "type" : "Apache License 2.0", + "type" : "Apache License 2.0", "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" }], "keywords": [ "Madoko", "Markdown", "LaTeX", - "Koka", + "Koka", "Microsoft", "Scholarly", "Academic", @@ -32,13 +32,13 @@ "readme.md", "license.txt" ], - "engines" : { - "node": ">=0.10.0" + "engines" : { + "node": ">=0.10.0" }, "dependencies": { - "amdefine" : ">=0.0.4", + "amdefine" : ">=0.0.4", "requirejs" : ">=2.1", - "mkdirp" : ">=0.3.5", + "mkdirp" : ">=1.0.0", "rimraf" : ">=2.4.4", "mime" : ">=1.3.4", "commander" : ">=2.8.1", @@ -52,7 +52,7 @@ "jake" : ">=0.7.6" }, "repository": { - "type": "hg", + "type": "hg", "url" : "https://hg.codeplex.com/madoko" } } diff --git a/support/madoko-local/readme.md b/support/madoko-local/readme.md index b51a1919..869da77c 100644 --- a/support/madoko-local/readme.md +++ b/support/madoko-local/readme.md @@ -21,10 +21,10 @@ the `madoko-local` program: ``` and that's it :-) -# Usage +# Usage Simply run the `madoko-local` program with the directory that you would -like to access as an argument. Everything in that directory, and all its +like to access as an argument. Everything in that directory, and all its sub-directories will be accessible to Madoko. Here we run it with access to the current directory: ``` @@ -70,10 +70,10 @@ Usage: Arguments: -* `mount-directory` - : The server only provides access to files and subdirectories under the - mount directory but not outside of it. If not given, the last specified - directory is used (which is stored in the local configuration file). +* `mount-directory` + : The server only provides access to files and subdirectories under the + mount directory but not outside of it. If not given, the last specified + directory is used (which is stored in the local configuration file). If this is the first run the current working directory is used. Options: @@ -90,10 +90,10 @@ Options: to generate bibliographies. This means you are no longer dependent on the server to run LaTeX for you. This flag requires that you have installed both Madoko (`npm install -g madoko`) and - LaTeX -- it is recommended to use the latest [TexLive] _simple_ (or _full_) + LaTeX -- it is recommended to use the latest [TexLive] _simple_ (or _full_) installation which is also used on the Madoko server. * `--verbose[=<n>]` - : Emit more tracing messages. + : Emit more tracing messages. Set `n` to 2 to be the most verbose. * `--secret[=secret]` : If no secret is provided, a new random secret is @@ -112,20 +112,20 @@ Options: specified `url`. Only specify trusted websites here since that website can obtain local disk access! * `--port=<n>` - : Use the specified port to serve `madoko-local`. - This can be useful if you have other servers running that - already use port 80. + : Use the specified port to serve `madoko-local`. + This can be useful if you have other servers running that + already use port 8080. * `--rundir=<dir>`, : The directory under which Madoko stores temporary files when - running Madoko (if the `--run` flag is present). Defaults to + running Madoko (if the `--run` flag is present). Defaults to `<homedir>/.madoko/run`. * `--runcmd=<cmd>`, : The command to use when running Madoko locally. By default this - is `madoko`. + is `madoko`. * `--runflags=<flags>`, : Extra flags to pass to the Madoko program when running locally. These flags are appended to the standard flags, namely: - `-vv -mmath-embed:512 --odir=out --sandbox`. + `-vv -mmath-embed:512 --odir=out --sandbox`. `madoko-local` stores the last generated secret and last used mount-directory in the local configuration file at @@ -137,12 +137,12 @@ When you pass the `--run` flag, the `madoko-local` program will not only serve files, but also run the local Madoko installation to generate PDF's, render mathematics, or generate the bibliography. This means that the Madoko web server is only used for the editing environment and updates --- all document processing will be done locally. +-- all document processing will be done locally. When running locally, `madoko-local` will store files temporarily under the `<rundir>/<temp-name>` directory where it runs Madoko with the `--sandbox` flag to restrict access to files under that -directory only. +directory only. When running Madoko locally, you need to have both Madoko and LaTeX installed. Madoko can be installed through the Node package manager as: @@ -151,12 +151,12 @@ Madoko can be installed through the Node package manager as: ``` For LaTeX, the latest [TexLive] full installation is recommended since it is also used on the Madoko server and it respects the `openin_any` and -`openout_any` settings which are needed to run LaTeX safely in a sandboxed mode. +`openout_any` settings which are needed to run LaTeX safely in a sandboxed mode. Also, for rendering of mathematics, you need to ensure you have `dvisvgm` version 1.14 or higher installed. Instructions on how to upgrade `dvisvgm` can be found in the [reference manual][dvisvgm]. -[dvisvgm]: http://research.microsoft.com/en-us/um/people/daan/madoko/doc/reference.html#dvisvgm +[dvisvgm]: http://research.microsoft.com/en-us/um/people/daan/madoko/doc/reference.html#dvisvgm # Security @@ -180,4 +180,4 @@ The server is designed with multiple security layers: [Madoko.net]: https://www.madoko.net "Madoko" [Node.js]: http://nodejs.org "Node.JS" -[TexLive]: https://www.tug.org/texlive "Tex Live" \ No newline at end of file +[TexLive]: https://www.tug.org/texlive "Tex Live" diff --git a/support/madoko-local/src/init.js b/support/madoko-local/src/init.js index aad14d08..46be753b 100644 --- a/support/madoko-local/src/init.js +++ b/support/madoko-local/src/init.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -43,9 +43,9 @@ var config = { configdir : null, // save config info here ($HOME/.madoko) logdir : null, // save log files here (<configdir>/log) mountdir : null, // the local directory to give access to. - port : 80, + port : 8080, origin : "https://www.madoko.net", - secret : null, + secret : null, verbose : 0, limits : { fileSize : 64*mb, @@ -77,7 +77,7 @@ Options .option("--runflags <opts>", "pass extra options <opts> to the madoko program") .option("--verbose [n]","output trace messages (0 none, 1 info, 2 debug)", parseInt ) .option("--rmdelay <secs>","delay before run directory is removed", parseInt ) - + Options.on("--help", function() { console.log([ " Notes:", @@ -102,7 +102,7 @@ function initializeConfig() { if (Options.homedir) config.homedir = Options.homedir; config.configdir = Util.combine(config.homedir, ".madoko"); config.logdir = Util.combine(config.configdir,"log"); - + // Try to read local config file var configFile = Path.join(config.configdir,"config.json"); var localConfig = Util.jsonParse(Util.readFileSync(configFile, {encoding:"utf8"}, "{}" )); @@ -116,7 +116,7 @@ function initializeConfig() { } else { // generate a secret - config.secret = Util.secureHash(12); + config.secret = Util.secureHash(12); // write back secret to localConfig... localConfig.secret = config.secret; @@ -140,9 +140,9 @@ function initializeConfig() { // Run if (Options.run) { - if (typeof Options.runcmd === "string") + if (typeof Options.runcmd === "string") config.run = Options.runcmd; - else + else config.run = "madoko"; if (typeof Options.runflags === "string") { @@ -212,4 +212,4 @@ return { initializeConfig: initializeConfig, }; -}); \ No newline at end of file +}); diff --git a/support/madoko-local/src/util.js b/support/madoko-local/src/util.js index b1496c3b..32a70ef6 100644 --- a/support/madoko-local/src/util.js +++ b/support/madoko-local/src/util.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -28,7 +28,7 @@ var dateFromISO = require("./date.js").dateFromISO; // ------------------------------------------------------------- -// Helpers +// Helpers // ------------------------------------------------------------- function startsWith(s,pre) { @@ -69,7 +69,7 @@ function properties(obj) { if (obj.hasOwnProperty(key)) { attrs.push(key); } - } + } return attrs; } @@ -89,13 +89,13 @@ function secureHash(len) { // ------------------------------------------------------------- // Wrap promises -// We use promises mostly to reliable catch exceptions +// We use promises mostly to reliable catch exceptions // ------------------------------------------------------------- function programDir() { - var m = module; - if (m==null) return ''; - while(m.parent) { m = m.parent; }; + var m = module; + if (m==null) return ''; + while(m.parent) { m = m.parent; }; return (m.filename ? m.filename : ''); }; @@ -111,7 +111,7 @@ function jsonParse(s,def) { function fileExistSync(fileName) { var stats = null; try { - stats = Fs.statSync(fileName); + stats = Fs.statSync(fileName); } catch(e) {}; return (stats != null); @@ -122,7 +122,7 @@ function readFileSync( fileName, options, defaultContent ) { return Fs.readFileSync(fileName,options); } catch(err) { - if (defaultContent !== undefined) + if (defaultContent !== undefined) return defaultContent; else throw err; @@ -146,7 +146,7 @@ function writeFileSync( fileName, content, options) { function ensureDir(dir) { - return new Promise( function(cont) { mkdirp(dir,cont); } ); + return mkdirp(dir); } // remove everything in dir recursively @@ -163,10 +163,13 @@ function writeFile( fpath, content, options ) { return new Promise( function(cont) { Fs.writeFile( fpath, content, options, function(err) { if (err && err.code === "ENOENT" && options.ensuredir) { - mkdirp( Path.dirname(fpath), function(err) { - if (err) return cont(err); - Fs.writeFile(fpath,content,options,cont); - }); + try { + mkdirp.sync( Path.dirname(fpath) ); + } + catch(err) { + return cont(err); + } + Fs.writeFile(fpath,content,options,cont); } else { cont(err); @@ -185,10 +188,13 @@ function appendFile( fpath, content, options ) { return new Promise( function(cont) { Fs.appendFile( fpath, content, options, function(err) { if (err && err.code === "ENOENT" && options.ensuredir) { - mkdirp( Path.dirname(fpath), function(err) { - if (err) return cont(err); - Fs.appendFile(fpath,content,options,cont); - }); + try { + mkdirp.sync( Path.dirname(fpath) ); + } + catch(err) { + return cont(err); + } + Fs.appendFile(fpath,content,options,cont); } else cont(err); }); @@ -217,9 +223,9 @@ function dnsReverse( ip ) { doms = null; console.log("unable to resolve ip: " + err.toString() ); } - cont(null,doms); + cont(null,doms); }); - }); + }); } function pathIsEqual( p1, p2 ) { @@ -273,11 +279,11 @@ return { appendFile : appendFile, readDir : readDir, fstat : fstat, - dnsReverse : dnsReverse, + dnsReverse : dnsReverse, pathIsEqual : pathIsEqual, // Errors HttpError : HttpError, }; -}); \ No newline at end of file +}); diff --git a/support/madoko-local/versionlog.json b/support/madoko-local/versionlog.json index 475eb1fa..59172f0d 100644 --- a/support/madoko-local/versionlog.json +++ b/support/madoko-local/versionlog.json @@ -1,5 +1,12 @@ { "log": [ + { "version": "0.9.1", + "date" : "2020-02-05", + "updates": [ + "Fix mkdirp incompatibility", + "Use port 8080 by default" + ] + }, { "version": "0.9", "date" : "2017-03-31", "updates": [ From 0b9879d13d5c22135132cc7c5a0554ecb5c4a5c5 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Wed, 5 Feb 2020 17:21:20 -0800 Subject: [PATCH 25/46] update styles for TexLive1019 --- styles/madoko.css | 39 +++++++++++++++--------- styles/madoko2.sty | 74 +++++++++++++++++++++++----------------------- 2 files changed, 62 insertions(+), 51 deletions(-) diff --git a/styles/madoko.css b/styles/madoko.css index ab7c4725..55af060c 100644 --- a/styles/madoko.css +++ b/styles/madoko.css @@ -70,7 +70,7 @@ text-align: left; } -.madoko .footnote { +.madoko .footnote { margin-left: 1em; } .madoko .footnote-before { @@ -184,9 +184,9 @@ } .madoko ul.list-style-type-dash > li:before { - content: "\2013"; + content: "\2013"; position: absolute; - margin-left: -1em; + margin-left: -1em; } /* Tables */ @@ -246,6 +246,17 @@ } +.madoko span.math-rendering { + display:inline-block; + width:8em; + overflow-x:auto; + font-size:70%; +} + +.madoko span.math-rendering::-webkit-scrollbar { + width:4px; +} + /*--------------------------------------------------------------------------- Default style for syntax highlighting ---------------------------------------------------------------------------*/ @@ -277,7 +288,7 @@ body.madoko, .madoko .serif { } -body.madoko { +body.madoko { -webkit-text-size-adjust: 100%; /* so math displays well on mobile devices */ text-rendering: optimizeLegibility; } @@ -285,7 +296,7 @@ body.madoko { body.madoko { max-width: 88ex; /* about 88 characters */ margin: 1em auto; - padding: 0em 2em; + padding: 0em 2em; } body.preview.madoko { @@ -297,24 +308,24 @@ body.preview.madoko { } /* style headings nicer, especially h5 and h6 */ -.madoko h1, .madoko h2, .madoko h3, .madoko h4 { - margin-top: 1.22em; +.madoko h1, .madoko h2, .madoko h3, .madoko h4 { + margin-top: 1.22em; margin-bottom: 1ex; } -.madoko h1+p, .madoko h2+p, .madoko h3+p, .madoko h4+p, .madoko h5+p { - margin-top: 1ex; +.madoko h1+p, .madoko h2+p, .madoko h3+p, .madoko h4+p, .madoko h5+p { + margin-top: 1ex; } -.madoko h5, .madoko h6 { +.madoko h5, .madoko h6 { margin-top: 1ex; font-size: 1em; } -.madoko h5 { +.madoko h5 { margin-bottom: 0.5ex; } .madoko h5 + p { margin-top: 0.5ex; } -.madoko h6 { +.madoko h6 { margin-bottom: 0pt; } .madoko h6 + p { @@ -323,7 +334,7 @@ body.preview.madoko { /* Fix monospace display (see http://code.stephenmorley.org/html-and-css/fixing-browsers-broken-monospace-font-handling/) */ -.madoko pre, .madoko code, .madoko kbd, .madoko samp, .madoko tt, +.madoko pre, .madoko code, .madoko kbd, .madoko samp, .madoko tt, .madoko .monospace, .madoko .token-indent, .madoko .reveal pre, .madoko .reveal code, .madoko .email { font-family: Consolas,"Andale Mono WT","Andale Mono",Lucida Console,Monaco,monospace,monospace; font-size: 0.85em; @@ -369,7 +380,7 @@ body.preview.madoko { @media only screen and (max-device-width:1024px) { body.madoko { - padding: 0em 0.5em; + padding: 0em 0.5em; } .madoko li { text-align: left; diff --git a/styles/madoko2.sty b/styles/madoko2.sty index 3f48c089..6a19ff83 100644 --- a/styles/madoko2.sty +++ b/styles/madoko2.sty @@ -1,6 +1,6 @@ %--------------------------------------------------------------------------- % Copyright 2013-2015 Microsoft Corporation. -% +% % This is free software; you can redistribute it and/or modify it under the % terms of the Apache License, Version 2.0. A copy of the License can be % found in the file "license.txt" at the root of this distribution. @@ -13,7 +13,7 @@ \RequirePackage{iftex} %------------------------------------------------------------- -% Process options +% Process options %------------------------------------------------------------- \options{% /madoko/.new family, @@ -41,7 +41,7 @@ \RequirePackage{longfbox} \RequirePackage{amsmath} \RequirePackage{amsfonts} -\RequirePackage{amssymb} +\RequirePackage{amssymb}\let\Bbbk\relax %circumvent error in TexLive 2019 \RequirePackage{stmaryrd} \RequirePackage{textcomp} \RequirePackage{pifont} @@ -76,7 +76,7 @@ %------------------------------------------------------------- -% Setup packages +% Setup packages %------------------------------------------------------------- % hyperref @@ -96,7 +96,7 @@ % enumitem \AtBeginDocument{% \ifdef\@enhook{% - \PackageWarning{madoko2}{You cannot use the package "enumerate" in madoko;\MessageBreak + \PackageWarning{madoko2}{You cannot use the package "enumerate" in madoko;\MessageBreak use the "enumitem" package instead\MessageBreak (which is available by default)}{}% }{}% } @@ -109,7 +109,7 @@ \providecommand\defcommand{\@ifstar\md@defcommand@S\md@defcommand@N} \newcommand\md@defcommand@S[1]{\let#1\outer\renewcommand*#1} -\newcommand\md@defcommand@N[1]{\let#1\outer\renewcommand#1} +\newcommand\md@defcommand@N[1]{\let#1\outer\renewcommand#1} \providecommand\provideenvironment{\@star@or@long\md@provide@environment} \newcommand\md@provide@environment[1]{% @@ -183,7 +183,7 @@ %------------------------------------------------------------- % Define a color name: either in terms a color specification or as a HTML color "\#XXXXXX" -% +% % \mddefinecolor[<default>]{<colorname>}{<colorspec|htmlcolor>} \def\md@colorhtml#1\relax#2\relax{\definecolor{#2}{HTML}{#1}} @@ -372,9 +372,9 @@ % Margins % --------------------------------------------------- -% calculate the auto margin based on the (border-box) width of the block and the width of +% calculate the auto margin based on the (border-box) width of the block and the width of % the other margin; use an empty other margin width if the other margin is also auto -% +% % \dimauto{<width>}{<other-margin>} \newcommand\dimauto[2]{% \ifblank{#2}{\dimeval{(\linewidth-#1)/2}}{\dimeval{\linewidth-#1-#2}}% @@ -385,14 +385,14 @@ \partopsep\z@ \trivlist \rightmargin=\dimdefault{#2}{\z@}% - \leftmargin=\dimdefault{#1}{\z@}% + \leftmargin=\dimdefault{#1}{\z@}% \advance\linewidth -\rightmargin \advance\linewidth -\leftmargin \advance\@totalleftmargin \leftmargin \parshape \@ne \@totalleftmargin \linewidth \item[]% }% -{\endtrivlist} +{\endtrivlist} %\AfterEndEnvironment{mdmarginlr}{\leavehmode} \newcommand*\mdmargintop[1]{\onnotzero{#1}{\par\addvskip{#1}}} @@ -401,18 +401,18 @@ \newlength{\dim@marginbottom} \newenvironment{mdbmargin}[1]{\begin{mdbmarginx}{#1}{#1}{#1}{#1}}{\end{mdbmarginx}} \newenvironment{mdbmarginx}[4]{% - \dim@marginbottom=\dimdefault{#3}{0pt}% + \dim@marginbottom=\dimdefault{#3}{0pt}% \mdmargintop{#1}% - \begin{mdmarginlr}{#4}{#2}% + \begin{mdmarginlr}{#4}{#2}% }{% \end{mdmarginlr}% - \mdmarginbottom{\dim@marginbottom}% + \mdmarginbottom{\dim@marginbottom}% } \newenvironment{mdbmargintb}[2]{% - \dim@marginbottom=\dimdefault{#2}{0pt}% + \dim@marginbottom=\dimdefault{#2}{0pt}% \mdmargintop{#1}% }{% - \mdmarginbottom{\dim@marginbottom}% + \mdmarginbottom{\dim@marginbottom}% } \newcommand*\mdpaddingtop[1]{\ifzero{#1}{}{\vspace*{#1}}} \newcommand*\mdpaddingbottom[1]{\ifzero{#1}{}{\vspace*{#1}}} @@ -471,11 +471,11 @@ \newcommand*\mdfontsize[1]{\fontsize{#1}{1.2\dimexpr#1\relax}\mdselectfont} \newcommand*\mdsetfontscale[2]{% \csdef{#1@Scale}{#2}% - \csdef{#1@scale}{#2}% + \csdef{#1@scale}{#2}% \eifstrequal{#1}{fi4}% inconsolata {\def\fifour@scaled{s*[#2]}}% {\eifstrequal{#1}{DejaVuSansMono-TLF}% - {\def\DejaVuSansMono@scale{#2}}% + {\def\DejaVuSansMono@scale{#2}}% {\eifstrequal{#1}{pcrs}% {\def\Cr@scale{#2}}% {}}}% @@ -517,7 +517,7 @@ \bgroup \expandafter\expandafter\expandafter\split@name\expandafter\string\font@name\@nil \let\md@selectedfamily\f@family - \egroupkeep\md@selectedfamily + \egroupkeep\md@selectedfamily % and test if the selected family is matching \eifstrequal{\md@selectedfamily}{#2}{}{% \toggletrue{md@fontsubstituted}% @@ -530,7 +530,7 @@ } \def\md@usefontfamily@#1#2\relax{% - \togglefalse{md@fontsubstituted}% + \togglefalse{md@fontsubstituted}% \if#1@\relax\relax \md@usefamily@#2/////\relax% \else\if#1!\relax\relax @@ -592,7 +592,7 @@ }% {\iffontchar\font32\relax% assume all fonts define a space character... \egroupkeep\md@fontselect - \def\md@fontitem{##1}% + \def\md@fontitem{##1}% \expandafter\listbreak \else \egroup @@ -605,7 +605,7 @@ \interactionmode=\md@interaction \ifx\md@fontselect\relax\relax% \ifx\md@fontsubst\relax\relax% - \PackageWarning{madoko2}{Could not find any font for font-family:'#1'}% + \PackageWarning{madoko2}{Could not find any font for font-family:'#1'}% \else \PackageWarning{madoko2}{Using substituted font for font-family: '\md@fontitemsubst'}% \let\md@fontselect\md@fontsubst @@ -702,7 +702,7 @@ {\addcontentsline{\csname ext@\@captype\endcsname}{\@captype}% {\protect\numberline{\md@captionlabel}{\protecting{\ignorespaces #1}}{}}}% {}% - #1% + #1% } \newcommand\mdfloatleft[1]{#1\hfill} @@ -725,7 +725,7 @@ %------------------------------------------------------------- % Table of contents %------------------------------------------------------------- -\ifdef\@tocrmarg{}{% +\ifdef\@tocrmarg{}{% % these are not always defined (for example in beamer) \def\@tocrmarg{2.55em}% \def\@pnumwidth{1.55em}% @@ -852,7 +852,7 @@ \newcommand\mathpre{\def\nobreakspace{\mathspace}} \newcommand\mdmathprefill{\hspace*{\linewidth minus \linewidth}} % flush left - + \newenvironment{mdmathpre}% {\setlength{\arraycolsep}{0pt}% \mathpre @@ -886,7 +886,7 @@ %------------------------------------------------------------- -% Tables +% Tables %------------------------------------------------------------- \newlength{\md@defaultcolwidth} \newcommand{\md@columncount}[3][0pt]{% @@ -1000,19 +1000,19 @@ {\begin{frame}\stepcounter{beamerpauses}}% {\end{frame}} -\newcommand\mdpause[1]{\pause #1} +\newcommand\mdpause[1]{\pause #1} \let\md@item\item% save original item command \newenvironment{mdfragmented}% {\def\md@enditem{\def\md@enditem{\end{onlyenv}}}% redefines itself at first use - \def\item{\md@enditem\begin{onlyenv}<+->\md@item}% + \def\item{\md@enditem\begin{onlyenv}<+->\md@item}% } {\end{onlyenv}} %------------------------------------------------------------- % Make Title % This is somewhat troublesome since almost all styles use -% different commands and command orders. This code tries to +% different commands and command orders. This code tries to % adapt to the most common styles. % Defines the sigplan "authorinfo" command for all other styles. %------------------------------------------------------------- @@ -1052,7 +1052,7 @@ \providerobustcmd{\@and}{\and} \providerobustcmd{\authorinfo}[3]{% \stepcounter{md@authorcount}% - \ifdef{\institute}{% + \ifdef{\institute}{% \protected@edef\md@more{#2#3}% \expandafter\ifx\md@more\relax\relax\instemailfalse\else\instemailtrue\fi% \ifnum\value{md@authorcount}>1% @@ -1063,7 +1063,7 @@ {%LLNCS, Beamer \gappto\md@authors{#1}% \ifinstemail% - \gappto\md@insts{\mdBrSep{#2}{\email{#3}}}% + \gappto\md@insts{\mdBrSep{#2}{\email{#3}}}% \fi% }% {% EPTCS @@ -1082,7 +1082,7 @@ \protected@edef\@more{#2#3}% \expandafter\ifx\@more\relax\relax \else \gappto\md@authors{\\[0.25ex]\mdBrSep{#2}{#3}}% - \fi + \fi }% \ifnum\value{md@authorcount}>1% \gappto\md@authorsx{, #1}% @@ -1113,7 +1113,7 @@ } % and finally maketitle -\newcommand{\mdmaketitle}[1]{% +\newcommand{\mdmaketitle}[1]{% \onnotblank{\md@titlex}{\mdtitlerunning{\md@titlex}}% \ifnum\value{md@authorcount}>0% \expandnext{\numberofauthors}{\arabic{md@authorcount}}% @@ -1126,7 +1126,7 @@ {%\gappto\md@Title{\titlenote{#1}}\title{\md@Title}} \mdsubtitle{\normalfont\normalsize #1}}% % {\date{#1}}% - \fi% + \fi% \maketitle } @@ -1148,11 +1148,11 @@ %------------------------------------------------------------- \newcommand\mdx@titlenote{} -\newcounter{mdx@authorcount} +\newcounter{mdx@authorcount} \newcommand\mdxtitle{\mdtitle} \newcommand{\mdxtitlenote}[1]{\gdef\mdx@titlenote{#1}} \newcommand{\mdxtitlefooter}[1]{\gappto{\md@authors}{\\[1ex]#1}} -\newcommand{\mdxsubtitle}[1]{\mdsubtitle{#1}} +\newcommand{\mdxsubtitle}[1]{\mdsubtitle{#1}} \newcommand{\mdxauthorname}[1]{\csgdef{@mdauthorname\the\value{mdx@authorcount}}{#1}} \newcommand{\mdxauthoraddress}[1]{\csgdef{@mdauthoraff\the\value{mdx@authorcount}}{#1}} \newcommand{\mdxauthoremail}[1]{\csgdef{@mdauthoremail\the\value{mdx@authorcount}}{#1}} @@ -1191,7 +1191,7 @@ %------------------------------------------------------------- %------------------------------------------------------------- -% Character commands +% Character commands %------------------------------------------------------------- \providecommand{\lsquot}{`} From 793f4a25c385860a60bc435987b0099bfafe032a Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Wed, 5 Feb 2020 17:24:27 -0800 Subject: [PATCH 26/46] update acmart style --- contrib/templates/acmart.cls | 394 +++++++++++++++++++---------- contrib/templates/acmart.mdk | 10 +- contrib/templates/style/acmart.mdk | 54 ++-- 3 files changed, 302 insertions(+), 156 deletions(-) diff --git a/contrib/templates/acmart.cls b/contrib/templates/acmart.cls index 0e057d25..ceb259aa 100644 --- a/contrib/templates/acmart.cls +++ b/contrib/templates/acmart.cls @@ -5,17 +5,17 @@ %% The original source files were: %% %% acmart.dtx (with options: `class') -%% +%% %% IMPORTANT NOTICE: -%% +%% %% For the copyright see the source file. -%% +%% %% Any modified versions of this file must be renamed %% with new filenames distinct from acmart.cls. -%% +%% %% For distribution of the original source see the terms %% for copying and modification in the file acmart.dtx. -%% +%% %% This generated file may be distributed as long as the %% original source files, as listed above, are part of the %% same distribution. (The sources need not necessarily be @@ -37,7 +37,7 @@ %% Right brace \} Tilde \~} \NeedsTeXFormat{LaTeX2e} \ProvidesClass{acmart} -[2017/09/16 v1.48 Typesetting articles for the Association for +[2018/08/12 v1.55 Typesetting articles for the Association for Computing Machinery] \def\@classname{acmart} \InputIfFileExists{acmart-preload-hook.tex}{% @@ -96,6 +96,18 @@ Computing Machinery] \fi}{\PackageError{\@classname}{The option authorversion can be either true or false}} \ExecuteOptionsX{authorversion=false} +\define@boolkey+{acmart.cls}[@ACM@]{nonacm}[true]{% + \if@ACM@nonacm + \PackageInfo{\@classname}{Using nonacm mode}% + \AtBeginDocument{\@ACM@printacmreffalse}% + % in 'nonacm' mode we disable the "ACM Reference Format" + % printing by default, but this can be re-enabled by the + % user using \settopmatter{printacmref=true} + \else + \PackageInfo{\@classname}{Not using nonacm mode}% + \fi}{\PackageError{\@classname}{The option nonacm can be either true or + false}} +\ExecuteOptionsX{nonacm=false} \define@boolkey+{acmart.cls}[@ACM@]{natbib}[true]{% \if@ACM@natbib \PackageInfo{\@classname}{Explicitly selecting natbib mode}% @@ -199,22 +211,6 @@ Computing Machinery] \ClassInfo{\@classname}{Using fontsize \ACM@fontsize} \LoadClass[\ACM@fontsize, reqno]{amsart} \RequirePackage{microtype} -\ifcase\ACM@format@nr - \relax % manuscript - \or % acmsmall - \or % acmlarge - \or % acmtog - \RequirePackage{flushend} - \or % sigconf - \RequirePackage{flushend} - \or % siggraph - \RequirePackage{flushend} - \or % sigplan - \RequirePackage{flushend} - \or % sigchi - \RequirePackage{flushend} - \or % sigchi-a -\fi \RequirePackage{etoolbox} \RequirePackage{refcount} \RequirePackage{totpages} @@ -264,7 +260,7 @@ Computing Machinery] \def\@tempb{compress}\ifx\@tempa\@tempb \def\NAT@cmprs{\@ne}\fi \def\@tempb{nocompress}\ifx\@tempa\@tempb - \def\NAT@cmprs{\@z}\fi + \def\NAT@cmprs{\z@}\fi \def\@tempb{sort&compress}\ifx\@tempa\@tempb \def\NAT@sort{\@ne}\def\NAT@cmprs{\@ne}\fi \def\@tempb{mcite}\ifx\@tempa\@tempb @@ -426,6 +422,32 @@ Computing Machinery] \def\l@section{\@tocline{1}{0pt}{1pc}{2pc}{}} \def\l@subsection{\@tocline{2}{0pt}{1pc}{3pc}{}} \def\l@subsubsection{\@tocline{2}{0pt}{1pc}{5pc}{}} +\def\@makefntext{\noindent\@makefnmark} +\if@ACM@sigchiamode +\long\def\@footnotetext#1{\marginpar{% + \reset@font\small + \interlinepenalty\interfootnotelinepenalty + \protected@edef\@currentlabel{% + \csname p@footnote\endcsname\@thefnmark + }% + \color@begingroup + \@makefntext{% + \rule\z@\footnotesep\ignorespaces#1\@finalstrut\strutbox}% + \color@endgroup}}% +\fi +\long\def\@mpfootnotetext#1{% + \global\setbox\@mpfootins\vbox{% + \unvbox\@mpfootins + \reset@font\footnotesize + \hsize\columnwidth + \@parboxrestore + \protected@edef\@currentlabel + {\csname p@mpfootnote\endcsname\@thefnmark}% + \color@begingroup\centering + \@makefntext{% + \rule\z@\footnotesep\ignorespaces#1\@finalstrut\strutbox}% + \color@endgroup}} +\def\@makefnmark{\hbox{\@textsuperscript{\normalfont\@thefnmark}}} \let\@footnotemark@nolink\@footnotemark \let\@footnotetext@nolink\@footnotetext \RequirePackage[bookmarksnumbered,unicode]{hyperref} @@ -458,14 +480,15 @@ Computing Machinery] filecolor=ACMDarkBlue} \else \hypersetup{hidelinks} - \fi} + \fi + \hypersetup{pdflang={English}, + pdfdisplaydoctitle}} \if@ACM@natbib \let\citeN\cite \let\cite\citep \let\citeANP\citeauthor \let\citeNN\citeyearpar \let\citeyearNP\citeyear - \let\citeyear\citeyearpar \let\citeNP\citealt \DeclareRobustCommand\citeA {\begingroup\NAT@swafalse @@ -477,7 +500,7 @@ Computing Machinery] \providecommand\citename[1]{#1}} \fi \newcommand\shortcite[2][]{% - \ifNAT@numbers\cite[#1]{#2}\else\citeyear[#1]{#2}\fi} + \ifNAT@numbers\cite[#1]{#2}\else\citeyearpar[#1]{#2}\fi} \def\bibliographystyle#1{% \ifx\@begindocumenthook\@undefined\else \expandafter\AtBeginDocument @@ -485,7 +508,8 @@ Computing Machinery] {\if@filesw \immediate\write\@auxout{\string\bibstyle{#1}}% \fi}} -\RequirePackage{graphicx, xcolor} +\RequirePackage{graphicx} +\RequirePackage[prologue]{xcolor} \definecolor[named]{ACMBlue}{cmyk}{1,0.1,0,0.1} \definecolor[named]{ACMYellow}{cmyk}{0,0.16,1,0} \definecolor[named]{ACMOrange}{cmyk}{0,0.42,1,0.01} @@ -602,32 +626,6 @@ Computing Machinery] \color@endgroup \egroup \expandafter\@iiiparbox\@mpargs{\unvbox\@tempboxa}} -\def\@makefntext{\noindent\@makefnmark} -\if@ACM@sigchiamode -\long\def\@footnotetext#1{\marginpar{% - \reset@font\small - \interlinepenalty\interfootnotelinepenalty - \protected@edef\@currentlabel{% - \csname p@footnote\endcsname\@thefnmark - }% - \color@begingroup - \@makefntext{% - \rule\z@\footnotesep\ignorespaces#1\@finalstrut\strutbox}% - \color@endgroup}}% -\fi -\long\def\@mpfootnotetext#1{% - \global\setbox\@mpfootins\vbox{% - \unvbox\@mpfootins - \reset@font\footnotesize - \hsize\columnwidth - \@parboxrestore - \protected@edef\@currentlabel - {\csname p@mpfootnote\endcsname\@thefnmark}% - \color@begingroup\centering - \@makefntext{% - \rule\z@\footnotesep\ignorespaces#1\@finalstrut\strutbox}% - \color@endgroup}} -\def\@makefnmark{\hbox{\@textsuperscript{\normalfont\@thefnmark}}} \def\@textbottom{\vskip \z@ \@plus 1pt} \let\@texttop\relax \RequirePackage{iftex} @@ -637,9 +635,9 @@ Computing Machinery] \pdfglyphtounicode{f_f_i}{FB03} \pdfglyphtounicode{f_f_l}{FB04} \pdfglyphtounicode{f_i}{FB01} -\pdfglyphtounicode{t_t}{00740074} -\pdfglyphtounicode{f_t}{00660074} -\pdfglyphtounicode{T_h}{00540068} +\pdfglyphtounicode{t_t}{0074 0074} +\pdfglyphtounicode{f_t}{0066 0074} +\pdfglyphtounicode{T_h}{0054 0068} \pdfgentounicode=1 \fi \RequirePackage{cmap} @@ -655,10 +653,14 @@ Computing Machinery] have the newtxmath package installed. Please upgrade your TeX}\@ACM@newfontsfalse} \if@ACM@newfonts -\RequirePackage[tt=false, type1=true]{libertine} + \RequirePackage[T1]{fontenc} +\ifxetex + \RequirePackage[tt=false]{libertine} +\else + \RequirePackage[tt=false, type1=true]{libertine} +\fi \RequirePackage[varqu]{zi4} \RequirePackage[libertine]{newtxmath} -\RequirePackage[T1]{fontenc} \fi \let\liningnums\@undefined \AtEndPreamble{% @@ -793,6 +795,7 @@ Computing Machinery] \define@choicekey*+{ACM}{acmJournal}[\@journalCode\@journalCode@nr]{% CIE,% CSUR,% + DTRAP,% IMWUT,% JACM,% JDIQ,% @@ -800,6 +803,7 @@ Computing Machinery] JERIC,% JETC,% JOCCH,% + PACMCGIT,% PACMHCI,% PACMPL,% POMACS,% @@ -810,10 +814,12 @@ Computing Machinery] TALLIP,% TAP,% TCPS,% + TDSCI,% TEAC,% TECS,% THRI,% TIIS,% + TIOT,% TISSEC,% TIST,% TKDD,% @@ -853,6 +859,10 @@ Computing Machinery] \def\@journalName{ACM Computing Surveys}% \def\@journalNameShort{ACM Comput. Surv.}% \def\@permissionCodeOne{0360-0300}% +\or % DTRAP + \def\@journalName{Digital Threats: Research and Practice}% + \def\@journalNameShort{Digit. Threat. Res. Pract.}% + \def\@permissionCodeOne{2576-5337}% \or % IMWUT \def\@journalName{Proceedings of the ACM on Interactive, Mobile, Wearable and Ubiquitous Technologies}% @@ -883,6 +893,12 @@ Computing Machinery] \or % JOCCH \def\@journalName{ACM Journal on Computing and Cultural Heritage}% \def\@journalNameShort{ACM J. Comput. Cult. Herit.}% +\or % PACMCGIT + \def\@journalName{Proceedings of the ACM on Computer Graphics and Interactive Techniques}% + \def\@journalNameShort{Proc. ACM Comput. Graph. Interact. Tech.}% + \def\@permissionCodeOne{2577-6193}% + \@ACM@screentrue + \PackageInfo{\@classname}{Using screen mode due to \@journalCode}% \or % PACMHCI \def\@journalName{Proceedings of the ACM on Human-Computer Interaction}% \def\@journalNameShort{Proc. ACM Hum.-Comput. Interact.}% @@ -924,6 +940,10 @@ Computing Machinery] \def\@journalName{ACM Transactions on Applied Perception}% \or % TCPS \def\@journalName{ACM Transactions on Cyber-Physical Systems}% +\or % TDSCI + \def\@journalName{ACM Transactions on Data Science}% + \def\@journalNameShort{ACM Trans. Data Sci.}% + \def\@permissionCodeOne{2577-3224}% \or % TEAC \def\@journalName{ACM Transactions on Economics and Computation}% \or % TECS @@ -938,6 +958,10 @@ Computing Machinery] \def\@journalName{ACM Transactions on Interactive Intelligent Systems}% \def\@journalNameShort{ACM Trans. Interact. Intell. Syst.}% \def\@permissionCodeOne{2160-6455}% +\or % TIOT + \def\@journalName{ACM Transactions on Internet of Things}% + \def\@journalNameShort{ACM Trans. Internet Things}% + \def\@permissionCodeOne{2577-6207}% \or % TISSEC \def\@journalName{ACM Transactions on Information and System Security}% \def\@journalNameShort{ACM Trans. Info. Syst. Sec.}% @@ -1102,7 +1126,9 @@ Computing Machinery] \fi \ifx\addresses\@empty \if@ACM@anonymous - \gdef\addresses{\@author{Anonymous Author(s)}}% + \gdef\addresses{\@author{Anonymous Author(s)% + \ifx\@acmSubmissionID\@empty\else\\Submission Id: + \@acmSubmissionID\fi}}% \gdef\authors{Anonymous Author(s)}% \else \gdef\addresses{\@author{#2}}% @@ -1116,7 +1142,9 @@ Computing Machinery] \fi \if@ACM@anonymous \ifx\shortauthors\@empty - \gdef\shortauthors{Anon.}% + \gdef\shortauthors{Anon. + \ifx\@acmSubmissionID\@empty\else Submission Id: + \@acmSubmissionID\fi}% \fi \else \def\@tempa{#1}% @@ -1332,7 +1360,8 @@ Computing Machinery] \acm@copyrightinput\acm@copyrightmode]{none,% acmcopyright,acmlicensed,rightsretained,% usgov,usgovmixed,cagov,cagovmixed,licensedusgovmixed,% - licensedcagov,licensedcagovmixed,othergov,licensedothergov}{% + licensedcagov,licensedcagovmixed,othergov,licensedothergov,% + iw3c2w3,iw3c2w3g}{% \@printpermissiontrue \@printcopyrighttrue \@acmownedtrue @@ -1346,13 +1375,13 @@ Computing Machinery] \fi \ifnum\acm@copyrightmode=3\relax % rightsretained \@acmownedfalse - \acmPrice{}% + \AtBeginDocument{\acmPrice{}}% \fi \ifnum\acm@copyrightmode=4\relax % usgov \@printpermissiontrue \@printcopyrightfalse \@acmownedfalse - \acmPrice{}% + \AtBeginDocument{\acmPrice{}}% \fi \ifnum\acm@copyrightmode=6\relax % cagov \@acmownedfalse @@ -1371,6 +1400,14 @@ Computing Machinery] \fi \ifnum\acm@copyrightmode=12\relax % licensedothergov \@acmownedfalse + \fi + \ifnum\acm@copyrightmode=13\relax % iw3c2w3 + \@acmownedfalse + \AtBeginDocument{\acmPrice{}}% + \fi + \ifnum\acm@copyrightmode=14\relax % iw3c2w3g + \@acmownedfalse + \AtBeginDocument{\acmPrice{}}% \fi} \def\setcopyright#1{\setkeys{ACM@}{acmcopyrightmode=#1}} \setcopyright{acmcopyright} @@ -1380,7 +1417,7 @@ Computing Machinery] Association for Computing Machinery. \or % acmlicensed Copyright held by the owner/author(s). Publication rights licensed to - the Association for Computing Machinery. + ACM\@. \or % rightsretained Copyright held by the owner/author(s). \or % usgov @@ -1392,18 +1429,24 @@ Computing Machinery] Association for Computing Machinery. \or %licensedusgovmixed Copyright held by the owner/author(s). Publication rights licensed to - the Association for Computing Machinery. + ACM\@. \or % licensedcagov Crown in Right of Canada. Publication rights licensed to - the Association for Computing Machinery. + ACM\@. \or %licensedcagovmixed Copyright held by the owner/author(s). Publication rights licensed to - the Association for Computing Machinery. + ACM\@. \or % othergov Association for Computing Machinery. \or % licensedothergov Copyright held by the owner/author(s). Publication rights licensed to - the Association for Computing Machinery. + ACM\@. + \or % ic2w3www + IW3C2 (International World Wide Web Conference Committee), published + under Creative Commons CC-BY~4.0 License. + \or % ic2w3wwwgoogle + IW3C2 (International World Wide Web Conference Committee), published + under Creative Commons CC-BY-NC-ND~4.0 License. \fi} \def\@formatdoi#1{\url{https://doi.org/#1}} \def\@copyrightpermission{% @@ -1517,7 +1560,18 @@ Computing Machinery] retains a nonexclusive, royalty-free right to publish or reproduce this article, or to allow others to do so, for Government purposes only. - \fi} + \or % iw3c2w3 + This paper is published under the Creative Commons Attribution~4.0 + International (CC-BY~4.0) license. Authors reserve their rights to + disseminate the work on their personal and corporate Web sites with + the appropriate attribution. + \or % iw3c2w3g + This paper is published under the Creative Commons + Attribution-NonCommercial-NoDerivs~4.0 International + (CC-BY-NC-ND~4.0) license. Authors reserve their rights to + disseminate the work on their personal and corporate Web sites with + the appropriate attribution. + \fi} \def\copyrightyear#1{\def\@copyrightyear{#1}} \copyrightyear{\@acmYear} \def\@teaserfigures{} @@ -1568,7 +1622,7 @@ Computing Machinery] \fi \fi \fi - \footnotetextcopyrightpermission{% + \if@ACM@nonacm\else\footnotetextcopyrightpermission{% \if@ACM@authordraft \raisebox{-2ex}[\z@][\z@]{\makebox[0pt][l]{\large\bfseries Unpublished working draft. Not for distribution.}}% @@ -1586,7 +1640,7 @@ Computing Machinery] \if@printcopyright \copyright\ \@copyrightyear\ \@copyrightowner\\ \else - \@copyrightyear.\ + \@copyrightyear.\ \fi \if@ACM@manuscript Manuscript submitted to ACM\\ @@ -1606,17 +1660,20 @@ Computing Machinery] , \@formatdoi{\@acmDOI}. \fi\\ \else - \if@ACM@journal - \@permissionCodeOne/\@acmYear/\@acmMonth-ART\@acmArticle - \ifx\@acmPrice\@empty\else\ \$\@acmPrice\fi\\ - \@formatdoi{\@acmDOI}% - \else % Conference - \ifx\@acmISBN\@empty\else ACM~ISBN~\@acmISBN - \ifx\@acmPrice\@empty.\else\dots\$\@acmPrice\fi\\\fi - \ifx\@acmDOI\@empty\else\@formatdoi{\@acmDOI}\fi% + \if@ACM@nonacm\else + \if@ACM@journal + \@permissionCodeOne/\@acmYear/\@acmMonth-ART\@acmArticle + \ifx\@acmPrice\@empty\else\ \$\@acmPrice\fi\\ + \@formatdoi{\@acmDOI}% + \else % Conference + \ifx\@acmISBN\@empty\else ACM~ISBN~\@acmISBN + \ifx\@acmPrice\@empty.\else\dots\$\@acmPrice\fi\\\fi + \ifx\@acmDOI\@empty\else\@formatdoi{\@acmDOI}\fi% + \fi \fi \fi \fi} + \fi \endgroup \setcounter{footnote}{0}% \@mkabstract @@ -1641,10 +1698,15 @@ Computing Machinery] \if@ACM@printacmref \@mkbibcitation \fi - \hypersetup{pdfauthor={\authors}, + \hypersetup{% + pdfauthor={\authors}, pdftitle={\@title}, pdfsubject={\@concepts}, - pdfkeywords={\@keywords}}% + pdfkeywords={\@keywords}, + pdfcreator={LaTeX with acmart + \csname ver@acmart.cls\endcsname\space + and hyperref + \csname ver@hyperref.sty\endcsname}}% \@printendtopmatter \@afterindentfalse \@afterheading @@ -2098,27 +2160,34 @@ Computing Machinery] \def\@pages@word{\ifnum\getrefnumber{TotPages}=1\relax page\else pages\fi}% \def\footnotemark{}% \def\\{\unskip{} \ignorespaces}% - \def\footnote{\ClassError{\@classname}{Please do note use footnotes + \def\footnote{\ClassError{\@classname}{Please do not use footnotes inside a \string\title{} or \string\author{} command! Use \string\titlenote{} or \string\authornote{} instead!}}% \def\@article@string{\ifx\@acmArticle\@empty{\ }\else, Article~\@acmArticle\ \fi}% \par\medskip\small\noindent{\bfseries ACM Reference Format:}\par\nobreak - \noindent\authors. \@acmYear. \@title + \noindent\bgroup + \def\\{\unskip{}, \ignorespaces}\authors\egroup. \@acmYear. \@title \ifx\@subtitle\@empty. \else: \@subtitle. \fi - \if@ACM@journal - \textit{\@journalNameShort} - \@acmVolume, \@acmNumber \@article@string (\@acmPubDate), - \ref{TotPages}~\@pages@word. - \else - In \textit{\@acmBooktitle}% - \ifx\@acmEditors\@empty\textit{.}\else - \andify\@acmEditors\textit{, }\@acmEditors~\@editorsAbbrev.% - \fi\ - ACM, New York, NY, USA% - \@article@string\unskip, \ref{TotPages}~\@pages@word. + \if@ACM@nonacm\else + % The 'nonacm' option disables 'printacmref' by default, + % and the present \@mkbibcitation definition is never used + % in this case. The conditional remains useful if the user + % explicitly sets \settopmatter{printacmref=true}. + \if@ACM@journal + \textit{\@journalNameShort} + \@acmVolume, \@acmNumber \@article@string (\@acmPubDate), + \ref{TotPages}~\@pages@word. + \else + In \textit{\@acmBooktitle}% + \ifx\@acmEditors\@empty\textit{.}\else + \andify\@acmEditors\textit{, }\@acmEditors~\@editorsAbbrev.% + \fi\ + ACM, New York, NY, USA% + \@article@string\unskip, \ref{TotPages}~\@pages@word. + \fi \fi - \@formatdoi{\@acmDOI} + \ifx\@acmDOI\@empty\else\@formatdoi{\@acmDOI}\fi \par\egroup} \def\@printendtopmatter{\par\bigskip} \def\@setthanks{\long\def\thanks##1{\par##1\@addpunct.}\thankses} @@ -2170,51 +2239,72 @@ Computing Machinery] Page \thepage\ of \@startPage--\pageref*{TotPages}.% } \fi -\def\@shortauthors{\if@ACM@anonymous Anon.\else\shortauthors\fi} +\def\@shortauthors{% + \if@ACM@anonymous + Anon. + \ifx\@acmSubmissionID\@empty\else Submission Id: \@acmSubmissionID\fi + \else\shortauthors\fi} \def\@headfootfont{\sffamily} \fancypagestyle{standardpagestyle}{% \fancyhf{}% \renewcommand{\headrulewidth}{\z@}% \renewcommand{\footrulewidth}{\z@}% + \def\@acmArticlePage{% + \ifx\@acmArticle\empty% + \if@ACM@printfolios\thepage\fi% + \else% + \@acmArticle\if@ACM@printfolios:\thepage\fi% + \fi% + } \ifcase\ACM@format@nr \relax % manuscript \fancyhead[LE]{\ACM@linecountL\if@ACM@printfolios\thepage\fi}% \fancyhead[RO]{\if@ACM@printfolios\thepage\fi}% \fancyhead[RE]{\@shortauthors}% \fancyhead[LO]{\ACM@linecountL\shorttitle}% - \fancyfoot[RO,LE]{\footnotesize Manuscript submitted to ACM}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize Manuscript submitted to ACM} + \fi% \or % acmsmall - \fancyhead[LE]{\ACM@linecountL\@headfootfont\@acmArticle\if@ACM@printfolios:\thepage\fi}% - \fancyhead[RO]{\@headfootfont\@acmArticle\if@ACM@printfolios:\thepage\fi}% + \fancyhead[LE]{\ACM@linecountL\@headfootfont\@acmArticlePage}% + \fancyhead[RO]{\@headfootfont\@acmArticlePage}% \fancyhead[RE]{\@headfootfont\@shortauthors}% \fancyhead[LO]{\ACM@linecountL\@headfootfont\shorttitle}% - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \fi% \or % acmlarge \fancyhead[LE]{\ACM@linecountL\@headfootfont - \@acmArticle\if@ACM@printfolios:\thepage\fi\quad\textbullet\quad\@shortauthors}% + \@acmArticlePage\quad\textbullet\quad\@shortauthors}% \fancyhead[LO]{\ACM@linecountL}% \fancyhead[RO]{\@headfootfont - \shorttitle\quad\textbullet\quad\@acmArticle\if@ACM@printfolios:\thepage\fi}% - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \shorttitle\quad\textbullet\quad\@acmArticlePage}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \fi% \or % acmtog \fancyhead[LE]{\ACM@linecountL\@headfootfont - \@acmArticle\if@ACM@printfolios:\thepage\fi\quad\textbullet\quad\@shortauthors}% + \@acmArticlePage\quad\textbullet\quad\@shortauthors}% \fancyhead[LO]{\ACM@linecountL}% \fancyhead[RE]{\ACM@linecountR}% \fancyhead[RO]{\@headfootfont - \shorttitle\quad\textbullet\quad\@acmArticle\if@ACM@printfolios:\thepage\fi\ACM@linecountR}% - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \shorttitle\quad\textbullet\quad\@acmArticlePage\ACM@linecountR}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: \@acmPubDate.}% + \fi% \else % Proceedings \fancyfoot[C]{\if@ACM@printfolios\footnotesize\thepage\fi}% \fancyhead[LO]{\ACM@linecountL\@headfootfont\shorttitle}% \fancyhead[RE]{\@headfootfont\@shortauthors\ACM@linecountR}% - \fancyhead[LE]{\ACM@linecountL\@headfootfont\acmConference@shortname, - \acmConference@date, \acmConference@venue}% - \fancyhead[RO]{\@headfootfont\acmConference@shortname, - \acmConference@date, \acmConference@venue\ACM@linecountR}% + \if@ACM@nonacm\else% + \fancyhead[LE]{\ACM@linecountL\@headfootfont\acmConference@shortname, + \acmConference@date, \acmConference@venue}% + \fancyhead[RO]{\@headfootfont\acmConference@shortname, + \acmConference@date, \acmConference@venue\ACM@linecountR}% + \fi% \fi \if@ACM@sigchiamode \fancyheadoffset[L]{\dimexpr(\marginparsep+\marginparwidth)}% @@ -2272,27 +2362,35 @@ Computing Machinery] \relax % manuscript \fancyhead[L]{\ACM@linecountL}% \fancyfoot[RO,LE]{\if@ACM@printfolios\small\thepage\fi}% - \fancyfoot[RE,LO]{\footnotesize Manuscript submitted to ACM}% + \if@ACM@nonacm\else% + \fancyfoot[RE,LO]{\footnotesize Manuscript submitted to ACM}% + \fi% \or % acmsmall - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: - \@acmPubDate.}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: + \@acmPubDate.}% + \fi% \fancyhead[LE]{\ACM@linecountL\@folioblob}% \fancyhead[LO]{\ACM@linecountL}% \fancyhead[RO]{\@folioblob}% \fancyheadoffset[RO,LE]{0.6\@folio@wd}% \or % acmlarge - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: - \@acmPubDate.}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: + \@acmPubDate.}% + \fi% \fancyhead[RO]{\@folioblob}% \fancyhead[LE]{\ACM@linecountL\@folioblob}% \fancyhead[LO]{\ACM@linecountL}% \fancyheadoffset[RO,LE]{1.4\@folio@wd}% \or % acmtog - \fancyfoot[RO,LE]{\footnotesize \@journalName, Vol. \@acmVolume, No. - \@acmNumber, Article \@acmArticle. Publication date: - \@acmPubDate.}% + \if@ACM@nonacm\else% + \fancyfoot[RO,LE]{\footnotesize \@journalNameShort, Vol. \@acmVolume, No. + \@acmNumber, Article \@acmArticle. Publication date: + \@acmPubDate.}% + \fi% \fancyhead[L]{\ACM@linecountL}% \fancyhead[R]{\ACM@linecountR}% \else % Conference proceedings @@ -2303,7 +2401,9 @@ Computing Machinery] \if@ACM@timestamp \ifnum\ACM@format@nr=0\relax % Manuscript \fancyfoot[LO,RE]{\ACM@timestamp\quad - \footnotesize Manuscript submitted to ACM} + \if@ACM@nonacm\else + \footnotesize Manuscript submitted to ACM + \fi} \else \fancyfoot[LO,RE]{\ACM@timestamp} \fi @@ -2317,26 +2417,56 @@ Computing Machinery] \let\ps@myheadings\ACM@ps@myheadings \let\ps@headings\ACM@ps@headings} \AtBeginDocument{\ACM@restore@pagestyle} +\def\ACM@NRadjust#1{% + \begingroup + \expandafter\ifx\csname Sectionformat\endcsname\relax + % do nothing when \Sectionformat is unknown + \def\next{\endgroup #1}% + \else + \def\next{\endgroup + \let\realSectionformat\Sectionformat + \def\ACM@sect@format@{#1}% + \let\Sectionformat\ACM@NR@adjustedSectionformat + %% next lines added 2018-06-17 to ensure section number is styled + \let\real@adddotafter\@adddotafter + \let\@adddotafter\ACM@adddotafter + #1{}% imposes the styles, but nullifies \MakeUppercase + \let\@adddotafter\real@adddotafter + }% + \fi \next +} +\def\ACM@NR@adjustedSectionformat#1#2{% + \realSectionformat{\ACM@sect@format{#1}}{#2}% + \let\Sectionformat\realSectionformat} +\DeclareRobustCommand{\ACM@sect@format}{\ACM@sect@format@} +\def\ACM@sect@format@null#1{#1} +\let\ACM@sect@format@\ACM@sect@format@null +\AtBeginDocument{% + \expandafter\ifx\csname LTX@adddotafter\endcsname\relax + \let\LTX@adddotafter\@adddotafter + \fi +} +\def\ACM@adddotafter#1{\ifx\relax#1\relax\else\LTX@adddotafter{#1}\fi} \renewcommand\section{\@startsection{section}{1}{\z@}% {-.75\baselineskip \@plus -2\p@ \@minus -.2\p@}% {.25\baselineskip}% - {\@secfont}} + {\ACM@NRadjust\@secfont}} \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% {-.75\baselineskip \@plus -2\p@ \@minus -.2\p@}% {.25\baselineskip}% - {\@subsecfont}} -\renewcommand\subsubsection{\@startsection{subsubsection}{3}{10pt}% + {\ACM@NRadjust\@subsecfont}} +\renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% {-.5\baselineskip \@plus -2\p@ \@minus -.2\p@}% {-3.5\p@}% - {\@subsubsecfont\@adddotafter}} + {\ACM@NRadjust{\@subsubsecfont\@adddotafter}}} \renewcommand\paragraph{\@startsection{paragraph}{4}{\parindent}% {-.5\baselineskip \@plus -2\p@ \@minus -.2\p@}% {-3.5\p@}% - {\@parfont\@adddotafter}} + {\ACM@NRadjust{\@parfont\@adddotafter}}} \renewcommand\part{\@startsection{part}{9}{\z@}% {-10\p@ \@plus -4\p@ \@minus -2\p@}% {4\p@}% - {\@parfont}} + {\ACM@NRadjust\@parfont}} \def\section@raggedright{\@rightskip\@flushglue \rightskip\@rightskip \leftskip\z@skip @@ -2464,8 +2594,9 @@ Computing Machinery] \@ifundefined{proposition}{% \newtheorem{proposition}[theorem]{Proposition} }{} + \@ifundefined{lemma}{% \newtheorem{lemma}[theorem]{Lemma} - \@ifundefined{lemma}{}{} + }{} \@ifundefined{corollary}{% \newtheorem{corollary}[theorem]{Corollary} }{} @@ -2538,4 +2669,3 @@ Computing Machinery] \endinput %% %% End of file `acmart.cls'. - diff --git a/contrib/templates/acmart.mdk b/contrib/templates/acmart.mdk index 01339890..f592a0df 100644 --- a/contrib/templates/acmart.mdk +++ b/contrib/templates/acmart.mdk @@ -39,6 +39,9 @@ But sad or merry, I must leave it now. Farewell! [TITLE] ~ +~TexRaw +\def\shorttitle{\acmshorttitle} +~ # Introduction { #sec-intro } @@ -70,7 +73,7 @@ Our contributions are: * A figure of a _butterfly_; * Some **mathematics**; * And some source code; -* And references to Tex books [@Knuth:TeX;@Lamport:LaTeX;@Goo93;@Fberg04] and others [@Grandstrand]. +* And references to Tex books [@Knuth:TeX;@Lamport:LaTeX;@Goo93;@Fberg04] and others [@Grandstrand]. # Content @@ -117,7 +120,7 @@ slumber Roads go ever ever on. &qed; ~ -~ Todo +~ Todo Finish the proof ~ @@ -149,7 +152,7 @@ with a flash like the lightning. "Dread has come upon you all! Alas! it has come more swiftly than I guessed." for i:=maxint to 0 do - begin + begin j:=square(root(i)); end; @@ -179,4 +182,3 @@ get out of it? and am I going to come back alive?" # Conclusion Really fun to write Markdown :-) - diff --git a/contrib/templates/style/acmart.mdk b/contrib/templates/style/acmart.mdk index bc279295..a5cfde32 100644 --- a/contrib/templates/style/acmart.mdk +++ b/contrib/templates/style/acmart.mdk @@ -17,19 +17,31 @@ Acm Article : 1 Acm Year : &conference-year; Acm Month : 1 Acm Copyright : none -Acm Copyright Year: &conference-year; -Acm Format : acmlarge +Acm Copyright Year: &conference-year; +Acm Format : acmsmall +Acm Number : ICFP +Acm Price : 15.00 +Acm Booktitle : &conference; +Short Title : &Title; Doc class : [&acm-format;]acmart.cls Bib Style : style/acm-reference-format.bst Cite Style : natural +@html Csl Style : madoko-natural + Pdf Latex : pdflatex +Package : [tt=false]libertine +Package : [varqu]zi4 Package : [libertine]newtxmath +Package : flushend + Author Columns : 1 Name Figure : Fig. + Tex Doc Header*: \acmConference[&conference-short;]{&conference;}{&conference-date;, &conference-year;}{&conference-location;} + \acmBooktitle{&acm-booktitle; (&conference-short;), &conference-date;, &conference-year;, &conference-location;} \acmISBN{&ISBN;} \acmDOI{&DOI;} \startPage{&start-page;} @@ -37,42 +49,46 @@ Tex Doc Header*: \renewcommand\authorinfo[3]{\author{#1}\affiliation{\institution{#2}}\email{#3}} \setcopyright{&acm-copyright;} \copyrightyear{&acm-copyright-year;} - \acmJournal{&acm-journal;} - \acmVolume{&acm-volume;} - \acmArticle{&acm-article;} + %\acmJournal{&acm-journal;} + %\acmVolume{&acm-volume;} + %\acmNumber{&acm-number;} + %\acmArticle{&acm-article;} \acmYear{&acm-year;} - \acmMonth{&acm-month;} + %\acmMonth{&acm-month;} + \acmPrice{&acm-price;} + \def\shorttitle{&short-title;} + \def\acmshorttitle{&short-title;} AcmClassification { input: texraw; } - + @if html { p { line-height: 1.3; } - authorname { + authorname { font-variant: small-caps; - .sans-serif; + .sans-serif; } titleblock, titleheader, authors { class: clear } author { text-align: left; padding: 0pt } - abstract { + abstract { before: clear; margin: 1em 0ex; - padding: 0.25em 0em; - border: 1px black solid; border-width: 1px 0pt; + padding: 0.25em 0em; + border: 1px black solid; border-width: 1px 0pt; } authorname,authoraddress,authoremail { display: inline } authorname,authoraddress { after: ", " } authorrow { margin: 0pt } - authors { margin-top: 1em } + authors { margin-top: 1em } } @if html && (acm-format != "sigplan") { - h1 { - font-variant: small-caps; - replace: "/[\s\S]*/\L\0/c"; + h1 { + font-variant: small-caps; + replace: "/[\s\S]*/\L\0/c"; } } @@ -81,14 +97,12 @@ AcmClassification { .sans-serif; } h1, authorname { - letter-spacing: 0.5px; + letter-spacing: 0.5px; } } @if html && ((acm-format=="acmlarge") || (acm-format=="acmtog")) { h1, h2 { - font-weight: normal; + font-weight: normal; } } - - \ No newline at end of file From f3c82f303813388ebef805a78b93ead598e907b4 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Wed, 5 Feb 2020 17:24:44 -0800 Subject: [PATCH 27/46] update version to 1.7 --- package.json | 4 ++-- src/version.kk | 2 +- versionlog.json | 27 +++++++++++++++++---------- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index a204afd5..6701b940 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.6", + "version": "1.1.7", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ @@ -48,6 +48,6 @@ }, "repository": { "type": "hg", - "url": "https://hg.codeplex.com/madoko" + "url": "https://github.com/koka-lang/madoko" } } diff --git a/src/version.kk b/src/version.kk index 1cfcaf1f..9033b457 100644 --- a/src/version.kk +++ b/src/version.kk @@ -10,4 +10,4 @@ module version // Do not edit the version, it is set in the Jakefile from package.json -public val version = "1.1.6" \ No newline at end of file +public val version = "1.1.7" \ No newline at end of file diff --git a/versionlog.json b/versionlog.json index ab79640a..1210c2a9 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,5 +1,12 @@ { "log" : [ + { "version": "1.1.7", + "date": "2020-02-05", + "updates": [ + "Fixes for TexLive 2019", + "Update ACMart style template, fix doi references" + ] + }, { "version": "1.1.6", "date": "2018-01-16", "updates": [ @@ -145,7 +152,7 @@ "Fix rendering of table-of-contents in LaTeX to do nicer line breaking", "Fix file path encoding in Dropbox for Chinese characters" ] - }, + }, { "version": "1.0.0-rc2", "date": "2016-01-11", "alert": "The standard prelude has changed: please re-load the 'prelude.mdk' in the file selection menu by pressing the grey reload icon (or delete it manually from the 'out' folder).", @@ -160,7 +167,7 @@ "Fix auto-scroll error in the preview", "Fix file path encoding in Onedrive for Chinese characters" ] - }, + }, { "version": "1.0.0-rc1", "date": "2016-01-04", "alert": "The standard prelude has changed: please re-load the 'prelude.mdk' in the file selection menu by pressing the grey reload icon (or delete it manually from the 'out' folder).", @@ -171,7 +178,7 @@ "Improved error locations for citations", "Various small bug fixes for CSL styles" ] - }, + }, { "version": "1.0.0-rc", "date": "2015-12-29", "alert": "The standard prelude has changed: please re-load the 'prelude.mdk' in the file selection menu by pressing the grey reload icons (or deleting them manually from the 'out' folder).", @@ -189,7 +196,7 @@ "date": "2015-12-15", "updates": [ "Support running Madoko/LaTex locally through [madoko-local](https://www.npmjs.com/package/madoko-local)", - "Support `page-break-before` and `page-break-after` attributes" + "Support `page-break-before` and `page-break-after` attributes" ] }, { "version": "0.9.22-beta", @@ -219,7 +226,7 @@ "Reduce mathematics and image size for HTML output by 2.", "Syntax highlighted tokens can now be fully styled within Madoko: use `.token.keyword { color: blue }` for example.", "Improved mathematics and preformatted code output for LaTeX.", - "Properly merge files that were deleted on the server.", + "Properly merge files that were deleted on the server.", "Add `Tex Doc Header` metadata key for including TeX commands after `\\begin{document}`.", "Improve handling of `display:none` elements", "Enable debugging of included Javascript fragments", @@ -245,7 +252,7 @@ "Improve LaTeX running times", "Enable microtype typography (character protrusion and font expansion) in PDF output", "Fix bug where images with uppercase extensions were not correctly embedded", - "Fix default Mathjax link for dynamic math" + "Fix default Mathjax link for dynamic math" ] }, { "version": "0.9.16-beta", @@ -272,7 +279,7 @@ "Fix bug where language definitions were not found on case-sensitive file systems.", "Improved error messages for missing fonts in LaTeX.", "Improved error locations for math definitions", - "Fixed bug where documents hosted on HTTP were not displayed correctly." + "Fixed bug where documents hosted on HTTP were not displayed correctly." ] }, { "version": "0.9.14-beta", @@ -281,7 +288,7 @@ "Improved performance of math and PDF generation.", "Use new Onedrive cloud storage API.", "Improved security of token management; re-logins may be required.", - "Improved CSRF mitigation and token revokation.", + "Improved CSRF mitigation and token revokation.", "Fixed bug in group replacement with `\\` escapes.", "Fixed bug in '&lt;!--meta' handling of whitespace." ] @@ -342,7 +349,7 @@ "The `References` header is now part of the `[BIB]` element for better compatibility with LaTeX; remove any explicit `References` header in older documents.", "Support for `CSS header` and `Tex Header` metadata keys.", "Support for sub-figures using `~SubFigure` and `~SubFigureRow`", - "Every madoko document now has a implicit `~Body` element around everything that can be styled though metadata rules." + "Every madoko document now has a implicit `~Body` element around everything that can be styled though metadata rules." ] }, { @@ -363,7 +370,7 @@ "Fix infinite reloading bug.", "Fixed syntax highlighting of `.md` files.", "Fix spell checking on IE.", - "Various fixes in LaTeX madoko style files." + "Various fixes in LaTeX madoko style files." ] } ] From 7850916b7156c8f8cf523780942d4c585fbc0945 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Wed, 5 Feb 2020 18:48:11 -0800 Subject: [PATCH 28/46] fix \Bbbk error --- styles/madoko2.sty | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/styles/madoko2.sty b/styles/madoko2.sty index 6a19ff83..a52c5427 100644 --- a/styles/madoko2.sty +++ b/styles/madoko2.sty @@ -41,7 +41,8 @@ \RequirePackage{longfbox} \RequirePackage{amsmath} \RequirePackage{amsfonts} -\RequirePackage{amssymb}\let\Bbbk\relax %circumvent error in TexLive 2019 +\let\Bbbk\relax %circumvent error in TexLive 2019 where amssymb redefines it +\RequirePackage{amssymb} \RequirePackage{stmaryrd} \RequirePackage{textcomp} \RequirePackage{pifont} From b3c0e531982a277d2072ed5fe2b342bff18bd99d Mon Sep 17 00:00:00 2001 From: Daan Leijen <daan@microsoft.com> Date: Sat, 8 Feb 2020 12:31:24 -0800 Subject: [PATCH 29/46] fix rmdirp version --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 6701b940..fa184b31 100644 --- a/package.json +++ b/package.json @@ -39,9 +39,9 @@ "node": ">=0.10.0" }, "dependencies": { - "amdefine": ">=0.0.4", - "requirejs": ">=2.1", - "mkdirp": ">=0.3.5" + "amdefine": "^1.0.1", + "mkdirp": "^0.3.5", + "requirejs": "^2.3.6" }, "devDependencies": { "jake": ">=0.7.6" From 999c2bc691efc082cf4499d351512ef82626c6bc Mon Sep 17 00:00:00 2001 From: Daan Leijen <daan@microsoft.com> Date: Sat, 8 Feb 2020 12:31:58 -0800 Subject: [PATCH 30/46] add support for inline math and code in tables containing `|` --- src/block.kk | 133 ++++++++++++++++++++++++++------------------------- src/main.kk | 18 +++---- 2 files changed, 77 insertions(+), 74 deletions(-) diff --git a/src/block.kk b/src/block.kk index ce175116..0c60d64d 100644 --- a/src/block.kk +++ b/src/block.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -17,16 +17,16 @@ import common import attributes /* -------------------------------------- - Block elements + Block elements ---------------------------------------- */ // A block element public type block { - HLine( attrs : attrs = attrsNone) + HLine( attrs : attrs = attrsNone) Blank( text : string ) // blank line (only used inside lists) Line( text : string, loose : bool = False, attrs : attrs = attrsNone ) // a single line of text (only used inside lists) Para( text : string, attrs : attrs = attrsNone ) - Code( text : string, language : string = "", attrs : attrs = attrsNone ) + Code( text : string, language : string = "", attrs : attrs = attrsNone ) Quote( content : list<block>, attrs : attrs = attrsNone ) List( tag : string, content : list<block>, attrs : attrs = attrsNone ) Item( content : list<block>, attrs : attrs = attrsNone ) @@ -41,7 +41,7 @@ public type block { } /* -------------------------------------- - Block grammar + Block grammar ---------------------------------------- */ // regular expression for content inside a start HTML tag @@ -57,7 +57,7 @@ val html = @"<(?:" + [closedTag,soloTag,comment].join("|") + @") *(?:\n+| // Attributes: allow escaped newline val rattrs = @"(?:<!--)?\{:?((?:[^\\'""\}\n]|\\[\s\S]|'(?:[^\\']|\\[\s\S])*'|""(?:[^""\\]|\\[\s\S])*"")*)\}(?:-->)?" // allow optional colon for maraku compat -val xattrs = rattrs + " *" +val xattrs = rattrs + " *" val iattrs = "(?:" + xattrs + ")?" // inline: headers and fences val pattrs = @"(?: {0,3}" + xattrs + ")?" // after a paragraph val battrs = @"(?:\n {0,3}" + xattrs + ")?" // after a block element @@ -70,22 +70,22 @@ val hr = @"(?:[*_\-](?: *[*_\-]){2,}) *" + iattrs + @"(?:\n+|$)" val bull = @"(?:[*+\-]|(?:\d+|[#iIaA])[\.\)])" val bullrest = @"(?:[*+\-]|(?:\d+|[#a-zA-Z]|" + lroman + "|" + uroman + @")[\.\)])" val lroman = @"(?:x?(?:i(?:v|x|i|ii)?|[vx]i{0,3}))" -val uroman = @"(?:X?(?:I(?:V|X|I|II)?|[VX]I{0,3}))" +val uroman = @"(?:X?(?:I(?:V|X|I|II)?|[VX]I{0,3}))" val endlist = hr + @"|\n(?! )(?!\1" + bull + @" )" val alist = @"( *)(" + bull + @") ([\s\S]+?)(?=\n(?:" + endlist + @")|$)\n?" -val endpara = @" *(?:<" + atag + @"|```|~+|>.|#{1,6} |\[" + xlinkid + @"\]:|" +val endpara = @" *(?:<" + atag + @"|```|~+|>.|#{1,6} |\[" + xlinkid + @"\]:|" + hr + @"|.+?\n *(?:===|---)" + ")" val notbspecialx= @"(?:[^ \[\*\-_\+\d><#`~\niIaA]|[iIaA](?!\.))" // not special block start sequence val notbspecial = @"(?:[^ \[\*\-_\+\d><#`~\niIaA]|[iIaA](?!\.))" // not special block start sequence // deflist -val defitem = @"(?:(?:\* (.*))|((?:" + notbspecial + @").*))\n\n?" +val defitem = @"(?:(?:\* (.*))|((?:" + notbspecial + @").*))\n\n?" val defstart = @"(?:[:]| {1,2}[~:])" val defdef = @"(?:.*(?:\n|$)(?:(?: .*)?(?:\n|$))*)" val defdefs = @"(?:" + defstart + defdef + ")+" -val deflist = @"^(?:" + defitem + defdefs + ")+" + pattrs +val deflist = @"^(?:" + defitem + defdefs + ")+" + pattrs // Tables val rxtableHeader = @"((?:(?:\| *|\+)[-=][-=+| ]*\n *)?(?:\| *[^-=~: ].+\n *)*)" @@ -94,16 +94,16 @@ val rxtableBody = @"((?: *(?:\||\+[-=]).*(?:\n|$))+)" // The main block grammar val blockGrammar : grammar<block,blockContext> = [ - Rule("lheading", regex(@"^(?! )([^ \n].*?)" + iattrs + @"\n *(===+|---+) *(?:\n+|$)"), blockHeadingLine ), + Rule("lheading", regex(@"^(?! )([^ \n].*?)" + iattrs + @"\n *(===+|---+) *(?:\n+|$)"), blockHeadingLine ), Rule("deflist", regex(deflist), blockDefList ), - Rule("parax", regex(@"^(" + notbspecial + @"[^|\n]*?(?:\n|$)(?:(?!" + endpara + @").+(?:\n|$))*)\n*"), blockPara ), + Rule("parax", regex(@"^(" + notbspecial + @"[^|\n]*?(?:\n|$)(?:(?!" + endpara + @").+(?:\n|$))*)\n*"), blockPara ), - Rule("code", regex(@"^( (?:.|\n+ )*)\n*"), blockCode ), + Rule("code", regex(@"^( (?:.|\n+ )*)\n*"), blockCode ), Rule("html", regex(@"^ *" + html), blockHtml ), Rule("deflink", regex(@"^ *\[(?!\^)" + xlinkid + @"\]: *<?((?:[^\\\s>]|\\(?:.|\n *))+)>?(?: +[""(]((?:[^\n\\]|\\(?:.|\n *))+)["")])?(?: |\\\n *)*" +iattrs+ @"(\n+|$)"), blockDefLink ), Rule("deffootnote",regex(@"^ *\[\^" + xlinkid + @"\]: *(?:\n {4})?(.*(?:\n+ {4}.*)*)(?:\n+|$)"), blockDefFootnote), Rule("hline", regex(@"^ *" + hr), blockHLine ), - + Rule("list", regex(@"^" + alist), blockList ), // before heading due to #. Rule("heading", regex(@"^ *(#{1,6})(0?) *(.+?) *#* *" + iattrs + @"(?:\n+|$)"), blockHeading ), Rule("blockquote", regex(@"^((?: *>.+(?:\n[^{\n]+)*\n*)+)" + battrs + "(?:\n+|$)"), blockQuote ), @@ -115,8 +115,8 @@ val blockGrammar : grammar<block,blockContext> = [ Rule("divnamed", regex(@"^ *(~+) *[Bb]egin +([\w\d\-]*) *" + iattrs + @"(?=\n)([\s\S]*?)\n *\1 *[Ee]nd +\2 *(?:\n+|$)"), blockDiv ), Rule("div", regex(@"^ *(~+) *([\w\d\-]*) *" + iattrs + @"(?=\n)([\s\S]*?)\n *\1 *(?:\n+|$)"), blockDiv ), - Rule("special", regex(@"^ *\[ *(TOC|FOOTNOTES|TITLE)(?: *= *([\w-]*))? *\] *\n*"), blockSpecial ), - Rule("para", regex(@"^(.+(?:\n|$)(?:(?!" + endpara + @").+(?:\n|$))*)\n*"), blockPara ), + Rule("special", regex(@"^ *\[ *(TOC|FOOTNOTES|TITLE)(?: *= *([\w-]*))? *\] *\n*"), blockSpecial ), + Rule("para", regex(@"^(.+(?:\n|$)(?:(?!" + endpara + @").+(?:\n|$))*)\n*"), blockPara ), Rule("blank", regex(@"^\n+"), blockBlank ), ruleLine, ] @@ -128,13 +128,13 @@ val blockListGrammar = blockGrammar.ruleReplace( True, ruleLineX ) .ruleReplace( True, ruleLine ) /* -------------------------------------- - The context for blocks + The context for blocks ---------------------------------------- */ // The parse context for block elements. rectype blockContext { // The parse context for block elements. Contains the "parse" function, - // and the current "grammar". + // and the current "grammar". BlockContext( grammar : grammar<block,blockContext>, listGrammar : grammar<block,blockContext>, citestyle : citestyle, @@ -150,9 +150,9 @@ rectype blockContext { /* -------------------------------------- - Block element functions + Block element functions ---------------------------------------- */ -function blockSpecial( cap : matched, context : blockContext ) : block { +function blockSpecial( cap : matched, context : blockContext ) : block { function joinnl( lines : list<string> ) : string { lines.map(fun(line) { line + "\n" }).join } @@ -161,7 +161,7 @@ function blockSpecial( cap : matched, context : blockContext ) : block { } function entry(name, className = name) { match(context.metadata[name]) { - Just(value) | value.trim != "" + Just(value) | value.trim != "" -> mkblock(className, ["&" + name + ";"]) _ -> [] } @@ -184,7 +184,7 @@ function blockSpecial( cap : matched, context : blockContext ) : block { } } function author( idx : int ) : list<string> { - mkblock("Author", + mkblock("Author", [entry("author" + idx.show, "AuthorName"), entry("affiliation" + idx.show, "AuthorAddress"), entry("address" + idx.show, "AuthorAddress"), @@ -217,7 +217,7 @@ function blockPara( cap : matched, _context ) : block { Para(txt, attrTxt.parseAttrs("P",txt) ) } val rxPattrs = regex(@"\n {0,3}" + xattrs + @"\s*$") - + function blockDefLink( cap : matched, context : blockContext ) : block { val id = cap.groups[1].definitionId DefLink(id, newLink(cap.groups[2].joinLines, cap.groups[3].joinLines, cap.groups[4].parseAttrs, id, context.bench )) @@ -254,18 +254,18 @@ function makeHeading(depth:int, source:string, attrStr : string) { function blockCode( cap : matched, context : blockContext ) : block { val source = cap.groups[1].replaceAll(rxindent4,"") - Code(source, "", + Code(source, "", if (!(context.pedantic || context.bench)) then attrsNone.addClass("pre-indented") else attrsNone) //if (context.bench) - // then Code(cap.groups[1].replaceAll(rxindent4,"")) + // then Code(cap.groups[1].replaceAll(rxindent4,"")) // else Div([], parseAttrs("","pre",cap.groups[1].replaceAll(rxindent4,""))) } function blockFencedCode( cap : matched, context : blockContext ) { val attrs = cap.groups[3].parseAttrs - Code(cap.groups[4], cap.groups[2], - if (!(context.pedantic || context.bench)) - then attrs.addClasses(["pre-fenced","pre-fenced" + cap.groups[1].length.show]) + Code(cap.groups[4], cap.groups[2], + if (!(context.pedantic || context.bench)) + then attrs.addClasses(["pre-fenced","pre-fenced" + cap.groups[1].length.show]) else attrs) } @@ -282,7 +282,7 @@ function blockDiv( cap : matched, context : blockContext ) : block { val cname = cap.groups[2].definitionId // if (cname=="htmlraw") then return Source(cap.groups[4],Raw(Just(FmtHtml)),attrsNone) log("customs","{ \"name\":" + cname.json + ", \"display\":" + cap.groups[2].json + "}"); - val attrs = cap.groups[3].parseAttrs(cname,source=cap.groups[4].substr(1)) + val attrs = cap.groups[3].parseAttrs(cname,source=cap.groups[4].substr(1)) Div( [], attrs ) } @@ -300,14 +300,14 @@ function blockDefFootnote( cap : matched, context : blockContext ) : block { function blockDefList( cap : matched, context : blockContext ) : block { function parseItem( extraAttrs : string, icap : matched, line : int, icontext : blockContext ) : list<block> { //trace("item:\n" + icap.matched) - val item = icap.groups[4].replaceAll(rxindent4,"") + val item = icap.groups[4].replaceAll(rxindent4,"") val attrs = icap.groups[3].parseAttrs("dd",source=item) val term = if (icap.groups[1]=="") then icap.groups[2] else icap.groups[1] val dterm = if (term=="") then [] else { val attrsd = parseAttrs(extraAttrs,"dt",source=term) [Div([Line(term)],attrsd)] } - val ddesc = Div(parseBlocks(icontext,item,line), + val ddesc = Div(parseBlocks(icontext,item,line), if (line <= 0) then attrs else attrs.setLineNo(context.lineMap,line)) [ddesc] + dterm } @@ -323,11 +323,11 @@ function blockList( cap : matched, context : blockContext ) : block { val tag = if (cap.groups[2].length > 1) then "ol" else "ul" function parseItem( extraAttrs : string, icap : matched, line : int, icontext : blockContext ) : list<block> { - val item = icap.groups[4].replaceAll(if (icontext.pedantic) + val item = icap.groups[4].replaceAll(if (icontext.pedantic) then rxindent4 - else rxindent(icap.groups[1].length) ,"") + else rxindent(icap.groups[1].length) ,"") val attrs = (extraAttrs + " ; " + icap.groups[3]).parseAttrs("li", item) - [Item(parseBlocks(icontext,item,line), + [Item(parseBlocks(icontext,item,line), if (line <= 0) then attrs else attrs.setLineNo(context.lineMap,line))] } @@ -347,16 +347,16 @@ function blockList( cap : matched, context : blockContext ) : block { } val rxitem = regex(@"^(( *)(?:" + bullrest + @") +)(?:" + iattrs + @")?(.*(?:\n(?!\2" + bullrest + @" ).*)*\n?)",multiline=True); -function blockListX( text : string, context : blockContext, +function blockListX( text : string, context : blockContext, tag : string, attrsInit : string, rxParseItem : regex, parseItem : (string,matched,int,blockContext) -> list<block> ) : block -{ +{ val loose = text.contains(blankline) // we should merge any text blocks into a paragraph for a loose list // in sane mode, we only make text blocks paragraphs if they are followed by a blank line - val icontext = context(grammar = // context.grammar.filter( fun(r) { !(r.name.startsWith("paragraph")) }), + val icontext = context(grammar = // context.grammar.filter( fun(r) { !(r.name.startsWith("paragraph")) }), context.listGrammar, loose = loose) // if (context.sane) then False else loose) - + val (txt,attrsTxt) = match(text.find(rxlattrs)) { Just(acap) -> (text.substr(0,text.length - acap.matched.length), attrsInit + " " + acap.groups[1]) @@ -370,22 +370,22 @@ function blockListX( text : string, context : blockContext, if (num==1) then attrs0 else attrs0.addKeyval("start",icap.groups[1]) } - } + } - val extraAttrs = attrs.classes.map( fun(cls) { "." + cls + "-li" } ).join(" ") + val extraAttrs = attrs.classes.map( fun(cls) { "." + cls + "-li" } ).join(" ") function parseItems( src : string, line : int, acc : list<block> = Nil ) { match (src.find(rxParseItem)) { Nothing -> acc.reverse Just(icap) -> { - val newline = if (line <= 0) then line else line + icap.matched.count("\n") + val newline = if (line <= 0) then line else line + icap.matched.count("\n") parseItems( src.substr1(icap.next), newline, parseItem(extraAttrs,icap,line,icontext) + acc) } } } - + // if loose then add a blank at the end of the last item so it has a Blank - val items = parseItems( txt + (if (loose) then "\n\n" else ""), context.lineNo ) + val items = parseItems( txt + (if (loose) then "\n\n" else ""), context.lineNo ) List(tag,items,attrs) } @@ -394,8 +394,8 @@ val blankline = regex(@"\n\n(?!\s*$)") val rxindent4 = regex(@"^ ",multiline=True) val rxFirstNum = regex(@"^ *(\d+)\.") -function rxindent(i : int ) : regex { - if (i==4) then rxindent4x +function rxindent(i : int ) : regex { + if (i==4) then rxindent4x elif (i==5) then rxindent5x elif (i==2) then rxindent2x elif (i==3) then rxindent3x @@ -414,8 +414,8 @@ function blockNpTable( cap : matched, context ) : block { val header = cap.groups[1].npcolumns(context,0) val cols = cap.groups[2].npcolumns(context,1).cells.map(fun(c) { c.text.columnStyle } ) val cells = cap.groups[3].rows.mapIndexed( fun(i,row) { npcolumns(row,context,i+2) } ) - Table([header],cols,cells,cap.groups[4].parseAttrs) -} + Table([header],cols,cells,cap.groups[4].parseAttrs) +} function blockTable( cap : matched, context ) : block { val headers = if (cap.groups[1]=="") then [] else cap.groups[1].rows.mapIndexed( fun(i,row) { columns(row,context,i) } ) @@ -425,11 +425,11 @@ function blockTable( cap : matched, context ) : block { // create a horizontal line after the headers? val txts = cols.map(source) val hline = if (txts.all(fun(txt){txt==""})) then [] else [Row(txts.map(fun(t){ Cell(t) }))] - Table(headers,cols,hline+cells,cap.groups[5].parseAttrs) -} + Table(headers,cols,hline+cells,cap.groups[5].parseAttrs) +} function npcolumns( row : string, context : blockContext, ofs : int ) : row { - ("|" + row + "|").columns(context,ofs) + ("|" + row + "|").columns(context,ofs) } function rows( body : string ) : list<string> { @@ -445,13 +445,16 @@ function columns( row : string, context : blockContext, ofs: int ) : row { Row(cells, if(context.lineNo > 0) then attrs.setLineNo(context.lineMap, context.lineNo + ofs) else attrs) } -val rxCell = regex(@"(?:^ *(?:\||\+(?=[-=])))?((?:[^\\|+]|\\.|\+ *(?![-=]))+(?:[|]+|[+]+(?= *[-=])|$))") +val rxCellCodeInline = @"(?:``(?:[^`]|`(?!`))*``|`(?:[^`]|``)*`)" +val rxCellTexInline = @"(?:\$\{((?:[^\\\$]|\\[\s\S])+)\$)" +val rxCellContent = @"(?:\\.|"+ rxCellTexInline + @"|" + rxCellCodeInline + @"|[^\\|+]|\+ *(?![-=]))" +val rxCell = regex(@"(?:^ *(?:\||\+(?=[-=])))?(" + rxCellContent + @"+(?:[|]+|[+]+(?= *[-=])|$))") val rxRowAttr = regex(@"[\+\|]" + xattrs + "$") // split the separator row in cells, and include the separators for determining the column style function columnStyles( row : string ) : list<attrs> { row.findAll(regex(@"[|+][^|+]+(?:[|+] *$)?")).list - .map(fun(cap) { cap.matched }).map(columnStyle) + .map(fun(cap) { cap.matched }).map(columnStyle) } function columnStyle( style : string ) : attrs { @@ -473,7 +476,7 @@ function columnStyle( style : string ) : attrs { /* -------------------------------------- - Parse a text into blocks + Parse a text into blocks ---------------------------------------- */ // Parse text into blocks @@ -490,8 +493,8 @@ public function parseBlocks( src : string, lineNo : int, lineMap : lineMap, // Parse text into blocks function parseBlocks( context : blockContext, src : string, line : int = 0 ) : list<block> { - val bs = if (line > 0) - then parseBlocksAccLine( context(lineNo = line), Nil, line, src ) + val bs = if (line > 0) + then parseBlocksAccLine( context(lineNo = line), Nil, line, src ) elif (context.lineNo > 0) then parseBlocksAccLine(context, Nil, context.lineNo, src) else parseBlocksAcc( context, Nil, src) @@ -501,22 +504,22 @@ function parseBlocks( context : blockContext, src : string, line : int = 0 ) : l function parseBlocksAcc( context : blockContext, acc : list<block>, src : string ) : list<block> { - if (src=="") return acc; + if (src=="") return acc; val (block,next,_) = matchRules(context.grammar,context,src,raw) parseBlocksAcc( context, Cons(block,acc), src.substr1(next) ) -} +} function parseBlocksAccLine( context : blockContext, acc : list<block>, line : int, src : string ) : list<block> { - if (src=="") return acc; + if (src=="") return acc; val (block,next,matched) = matchRules(context.grammar,context(lineNo=line),src,raw) val line2 = line + matched.count("\n") val ofs = match(matched.find(rxPreWhite)) { Nothing -> 0 Just(cap) -> cap.regex/matched.count("\n") } - val block2 = block.adjustAttrs( fun(attrs:attrs) { - attrs.setLineNo(context.lineMap,line+ofs) + val block2 = block.adjustAttrs( fun(attrs:attrs) { + attrs.setLineNo(context.lineMap,line+ofs) }) parseBlocksAccLine( context, Cons(block2,acc), line2, src.substr1(next) ) } @@ -529,7 +532,7 @@ function raw( s : string ) : block { public function setLineNo( attrs: attrs, lineMap: lineMap, lineNo : int, overwrite : bool = False ) : attrs { val srcline = translateLine(lineMap,lineNo) - val attrs1 = if (!overwrite && attrs.hasKey("data-line").bool) then attrs else attrs.setLineNo(lineNo,srcline) + val attrs1 = if (!overwrite && attrs.hasKey("data-line").bool) then attrs else attrs.setLineNo(lineNo,srcline) if(!attrs1.input.isMarkdown || attrs1.hasClass("pre-fenced")) { val firstline = translateLine(lineMap,lineNo+1) attrs1.addKeyval("data-line-first",firstline) @@ -538,23 +541,23 @@ public function setLineNo( attrs: attrs, lineMap: lineMap, lineNo : int, overwri } -function adjustAttrs( b : block, adjust : (attrs : attrs) -> attrs ) : block +function adjustAttrs( b : block, adjust : (attrs : attrs) -> attrs ) : block { match(b) { HLine( attrs ) -> HLine(adjust(attrs)) Line( text,loose, attrs) -> Line(text,loose,adjust(attrs)) Para( text, attrs ) -> Para(text,adjust(attrs)) - Code( text, language, attrs) -> Code( text, language, adjust(attrs)) + Code( text, language, attrs) -> Code( text, language, adjust(attrs)) Quote( content, attrs) -> Quote( content, adjust(attrs)) List( tag, content, attrs) -> List( tag, content, adjust(attrs)) Item( content, attrs) -> Item( content, adjust(attrs)) Heading( depth, text, attrs) -> Heading( depth, text, adjust(attrs)) Table( header, columnAttrs, cells, attrs) -> Table( header, columnAttrs, cells, adjust(attrs)) Div( content, attrs) -> Div( content, adjust(attrs)) - Source( text, input, attrs) -> Source( text, input, adjust(attrs)) + Source( text, input, attrs) -> Source( text, input, adjust(attrs)) //DefLink( id, link) -> //DefFootnote( id, content) -> - //Empty() -> + //Empty() -> Special( name, value, attrs) -> Special(name,value, adjust(attrs))// Special blocks, like [TOC] or [FOOTNOTES] _ -> b } diff --git a/src/main.kk b/src/main.kk index e101a453..555c0627 100644 --- a/src/main.kk +++ b/src/main.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -10,7 +10,7 @@ module main import std/path -import std/regex +import std/regex import common import options import storage @@ -32,7 +32,7 @@ public function test( s : string = "code_blocks", moreargs = "" ) { val outputDir = "test/out" val target = root + ".html" - markdownFiles("-v --tex --installdir=src --odir=" + outputDir + " " + moreargs + " " + input) + markdownFiles("-v --tex --installdir=src --odir=" + outputDir + " " + moreargs + " " + input) fun(outText,_input,output,_options) { //trace("-----\n" + outText + "\n-----") val targetText = target.readTextFileDef("") @@ -56,30 +56,30 @@ public function test( s : string = "code_blocks", moreargs = "" ) { trace("*** test success (modulo whitespace)") } //println("done!") - } + } } val runners = Runners( runLatex/runPdfLaTeX, runLatex/runBibtex, mathStaticRun/runMathStatic, runLatex/runZip ) -public function markdownFiles(args : string, action : (string,string,string,options) -> io ()) : io () { +public function markdownFiles(args : string, action : (string,string,string,options) -> io ()) : io () { val mbopts = parseOptions(version,args); match(mbopts) { Nothing -> () Just(opts) -> { if (opts.outputDir != "" && !(fexistsSync(opts.outputDir))) { mkdirp(opts.outputDir) - } + } opts.inputs.foreach fun(input0) { val input = if (input0.extname=="") then input0 + ".mdk" else input0 - val outName = outputName(input,opts) - if (opts.options.verbose > 0) println("process: " + input + " -> " + outName ) + val outName = outputName(input,opts) + if (opts.options.verbose > 0) println("process: " + input + " -> " + outName ) match (input.tryReadTextFile()) { Left -> println("error: unable to read: " + input ) Right(content) -> { processContent(input, outName, content, opts, True, runners, action) } - } + } } // foreach } } From 6fc6d9a9279b6d00eb25187a87deb690af9c5e2c Mon Sep 17 00:00:00 2001 From: daan <daan@microsoft.com> Date: Sat, 8 Feb 2020 20:38:31 +0000 Subject: [PATCH 31/46] fix decipher on new nodejs --- web/server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web/server.js b/web/server.js index 873e6a42..9ae00491 100644 --- a/web/server.js +++ b/web/server.js @@ -831,6 +831,7 @@ function encrypt(secret,value) { function decrypt(secret,value) { var decipher = crypto.createDecipher('aes256', secret); + //decipher.setAutoPadding(false); var decrypted = decipher.update(value, 'base64', 'utf8') + decipher.final('utf8'); return decrypted; } From 8e9f15d1dd7ed7403bc68e46841b99453c8c320b Mon Sep 17 00:00:00 2001 From: daan <daan@microsoft.com> Date: Sat, 8 Feb 2020 20:39:21 +0000 Subject: [PATCH 32/46] fix github login and repo listinq --- web/client/scripts/remote-github.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/web/client/scripts/remote-github.js b/web/client/scripts/remote-github.js index 15a6c286..dff80f9b 100644 --- a/web/client/scripts/remote-github.js +++ b/web/client/scripts/remote-github.js @@ -16,7 +16,7 @@ var github = new OAuthRemote( { loginUrl : "https://github.com/login/oauth/authorize", loginParams : { client_id: "9c24f7ac71d1ede26ba3", - scope : "repo", + scope : "repo,read:org", }, logoutUrl : "https://github.com/logout", logoutTimeout: 5000, @@ -54,6 +54,9 @@ function getItems( repoPath, branch, tpath, full ) { function getAllRepos() { + // used to need to list over all orgs but in v3 all accessible repositories are returned by default for a user. */ + return getRepos(); + /* return github.requestGET( { url: "user/orgs", cache: 10000 }, { per_page:100 } ).then(function(orgs) { var getrepos = orgs.map( function(org) { return github.requestGET( { url: org.url + "/repos", cache: 10000 }, { per_page:100 } ); } ); getrepos.unshift( getRepos() ); @@ -61,6 +64,7 @@ function getAllRepos() { return [].concat.apply([],reposs); }); }); + */ } function splitPath(path) { From e14ee957695405642cb1cb757b6515279cd77d7d Mon Sep 17 00:00:00 2001 From: daan <daan@microsoft.com> Date: Sat, 8 Feb 2020 20:39:40 +0000 Subject: [PATCH 33/46] update log message --- versionlog.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/versionlog.json b/versionlog.json index ab79640a..2c48d3f6 100644 --- a/versionlog.json +++ b/versionlog.json @@ -4,7 +4,8 @@ "date": "2018-01-16", "updates": [ "Fix handling of deleted files on Dropbox v2", - "Limit number of concurrent write requests on Dropbox" + "Limit number of concurrent write requests on Dropbox", + "Fix login to github" ] }, { "version": "1.1.5", From f4d96a238417b1ccb8f308ddc7b8af8e7bd0c028 Mon Sep 17 00:00:00 2001 From: Daan Leijen <daan@microsoft.com> Date: Sat, 8 Feb 2020 12:46:20 -0800 Subject: [PATCH 34/46] update version --- package.json | 2 +- src/version.kk | 2 +- versionlog.json | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fa184b31..0621e28c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.7", + "version": "1.1.8", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/version.kk b/src/version.kk index 9033b457..0bdbf462 100644 --- a/src/version.kk +++ b/src/version.kk @@ -10,4 +10,4 @@ module version // Do not edit the version, it is set in the Jakefile from package.json -public val version = "1.1.7" \ No newline at end of file +public val version = "1.1.8" \ No newline at end of file diff --git a/versionlog.json b/versionlog.json index 1210c2a9..3dcf9424 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,5 +1,11 @@ { "log" : [ + { "version": "1.1.8", + "date": "2020-02-08", + "updates": [ + "Allow inline math and code in table cells containing | (must be braced for math, e.g. ${...|...}$)" + ] + }, { "version": "1.1.7", "date": "2020-02-05", "updates": [ From b341e21c0e07bdc69227c39594afa9cda6ed42a2 Mon Sep 17 00:00:00 2001 From: Daan Leijen <daan@microsoft.com> Date: Sat, 8 Feb 2020 13:50:16 -0800 Subject: [PATCH 35/46] fix table reformatting in the web UI with math/code that contains | --- web/client/scripts/ui.js | 941 ++++++++++++++++++++------------------- 1 file changed, 473 insertions(+), 468 deletions(-) diff --git a/web/client/scripts/ui.js b/web/client/scripts/ui.js index e00c227c..dc535ab0 100644 --- a/web/client/scripts/ui.js +++ b/web/client/scripts/ui.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -32,7 +32,7 @@ document.addEventListener( "keydown", function(ev) { if (ev.ctrlKey) code |= KeyMask.ctrlKey; if (ev.altKey) code |= KeyMask.altKey; if (ev.metaKey) code |= KeyMask.metaKey; - if (ev.shiftKey) code |= KeyMask.shiftKey; + if (ev.shiftKey) code |= KeyMask.shiftKey; keyHandlers.forEach( function(handler) { if (handler.code === code) { if (handler.stop) { @@ -65,10 +65,10 @@ var localStorageLimit = 5000000; // (~5mb) function localStorageSave( fname, obj, createMinimalObj ) { var key = "local/" + fname; if (!localStorage) { - Util.message("cannot make local backup: upgrade your browser.", Util.Msg.Error ); + Util.message("cannot make local backup: upgrade your browser.", Util.Msg.Error ); return false; } - try { + try { localStorage.setItem( key, JSON.stringify(obj) ); return true; } @@ -97,7 +97,7 @@ function localStorageLoad( fname ) { } catch(e) { return null; - } + } } function getModeFromExt(ext) { @@ -106,9 +106,9 @@ function getModeFromExt(ext) { var origin = window.location.origin ? window.location.origin : window.location.protocol + "//" + window.location.host; -var State = { Normal:"normal", - Loading:"loading", - Init:"initializing", +var State = { Normal:"normal", + Loading:"loading", + Init:"initializing", Syncing:"synchronizing", Exporting:"exporting" } @@ -120,7 +120,7 @@ var UI = (function() { self.state = State.Init; self.editor = null; self.app = document.getElementById("main"); - + self.refreshRate = 500; self.serverRefreshRate = 2500; self.runner = runner; @@ -144,33 +144,33 @@ var UI = (function() { }; //Monaco.Editor.createCustomMode(MadokoMode.mode); - window.onbeforeunload = function(ev) { + window.onbeforeunload = function(ev) { //if (self.storage.isSynced()) return; localStorage.removeItem("viewer-html"); localStorage.removeItem("viewer-scroll"); self.saveSettings(); - if (self.localSave()) return; + if (self.localSave()) return; var message = "Changes to current document have not been saved yet!\n\nIf you leave this page, any unsaved work will be lost."; (ev || window.event).returnValue = message; return message; }; - var firstTime = localStorage.settings === undefined; + var firstTime = localStorage.settings === undefined; self.loadSettings(); self.initUIElements("",firstTime); - + self.loadFromHash().then( function() { - // Initialize madoko and madoko-server runner - self.initRunners(); + // Initialize madoko and madoko-server runner + self.initRunners(); }).then( function() { }, function(err) { - Util.message(err, Util.Msg.Error); + Util.message(err, Util.Msg.Error); }).always( function() { self.state = State.Normal; }); } UI.prototype.reload = function(force) { - var self = this; + var self = this; if (Localhost.localhost.hosted) { Localhost.localhost.reload(force); } @@ -200,7 +200,7 @@ var UI = (function() { return; } else if (state) { - self.state = state; + self.state = state; } } try { @@ -220,7 +220,7 @@ var UI = (function() { if (state) self.state = State.Normal; if (status) Util.message( status, Util.Msg.Status); return res; - } + } } catch(exn) { if (state) self.state = State.Normal; @@ -257,7 +257,7 @@ var UI = (function() { }); } Util.forEachProperty(defaultCheckBoxes, function(name,checked) { - initCheckbox(name,checked); + initCheckbox(name,checked); }); } @@ -267,7 +267,7 @@ var UI = (function() { if (!lsettings) { // legacy var json = localStorage.getItem("settings"); - if (!json) { + if (!json) { json = localStorage.getItem("local/local"); } if (json) lsettings = Util.jsonParse(json,{}); @@ -279,7 +279,7 @@ var UI = (function() { theme : "vs", fontScale : "medium" }); - + if (lsettings) { self.updateSettings( lsettings ); } @@ -289,7 +289,7 @@ var UI = (function() { if (cap) { var json = decodeURIComponent(cap[1]); self.updateSettings( Util.jsonParse(json,{}) ); - } + } } UI.prototype.updateSettings = function(obj) { @@ -325,22 +325,22 @@ var UI = (function() { // set value self.settings[name] = value; self.saveSettings(); - + // special actions if (name==="theme") { if (self.editor) self.editor.updateOptions( { theme: self.getCurrentTheme() }); self.app.setAttribute("data-theme",value); } else if (name==="wrapLines") { - if (self.editor) self.editor.updateOptions( { wrappingColumn: (value ? 0 : -1 ) } ); + if (self.editor) self.editor.updateOptions( { wrappingColumn: (value ? 0 : -1 ) } ); } else if (name==="lineNumbers") { - if (self.editor) self.editor.updateOptions( { lineNumbers: value } ); + if (self.editor) self.editor.updateOptions( { lineNumbers: value } ); } else if (name==="disableAutoUpdate" && self.asyncMadoko) { if (value) { self.asyncMadoko.pause(); - } + } else { self.asyncMadoko.resume(); } @@ -355,7 +355,7 @@ var UI = (function() { } else if (name==="fontScale") { if (self.editor) { - var editView = self.editor.getView(); + var editView = self.editor.getView(); var lines = editView.viewLines; var rng = lines._currentVisibleRange; var midLine = Math.round(rng.startLineNumber + ((rng.endLineNumber - rng.startLineNumber + 1)/2)); @@ -367,7 +367,7 @@ var UI = (function() { else if (name==="viewFull") { var view = value ? "full" : "normal"; self.app.setAttribute("data-view",view); - self.dispatchViewEvent( { eventType: "view", view: view } ); + self.dispatchViewEvent( { eventType: "view", view: view } ); setTimeout( function(ev) { Util.dispatchEvent(window,"resize"); }, 100 ); } } @@ -393,11 +393,11 @@ var UI = (function() { // common elements self.usersStatus = document.getElementById("users-status"); self.usersPanel = document.getElementById("users-panel"); - self.spinner = document.getElementById("view-spinner"); + self.spinner = document.getElementById("view-spinner"); self.spinner.spinDelay = 750; - self.syncer = document.getElementById("sync-spinner"); - self.syncer.spinDelay = 100; - self.exportSpinner = document.getElementById("export-spinner"); + self.syncer = document.getElementById("sync-spinner"); + self.syncer.spinDelay = 100; + self.exportSpinner = document.getElementById("export-spinner"); self.exportSpinner.spinDelay = 1000; self.view = document.getElementById("view"); self.editSelectHeader = document.getElementById("edit-select-header"); @@ -408,20 +408,20 @@ var UI = (function() { self.lastRenderWasSlow = false; self.lastViewRenderWasSlow = false; - // listen to application cache - self.appUpdateReady = false; - if (window.applicationCache.status === window.applicationCache.UPDATEREADY) { - // reload immediately if an update is ready + // listen to application cache + self.appUpdateReady = false; + if (window.applicationCache.status === window.applicationCache.UPDATEREADY) { + // reload immediately if an update is ready self.reload(true); } else { window.applicationCache.addEventListener( "updateready", function(ev) { - if (window.applicationCache.status === window.applicationCache.UPDATEREADY) { + if (window.applicationCache.status === window.applicationCache.UPDATEREADY) { if (!self.appUpdateReady) { window.applicationCache.swapCache(); self.appUpdateReady = true; - } - } + } + } }); } @@ -451,7 +451,7 @@ var UI = (function() { //arrowSize: 10, }, quickSuggestions: true, - }); + }); self.lastActivity = 0; @@ -459,9 +459,9 @@ var UI = (function() { window.addEventListener("click",function(ev) { self.lastActivity = ev.timeStamp || Date.now(); }); - + Util.onResize( function() { - self.editor.layout(); + self.editor.layout(); self.syncView({force: true}); self.lastActivity = Date.now(); }); @@ -470,14 +470,14 @@ var UI = (function() { // synchronize on scrolling self.syncInterval = 0; - self.editor.addListener("scroll", function (ev) { - function scroll() { + self.editor.addListener("scroll", function (ev) { + function scroll() { self.anonEvent( function() { - var scrolled = self.syncView({editorScroll:true}); + var scrolled = self.syncView({editorScroll:true}); if (!scrolled) { clearInterval(self.syncInterval); self.syncInterval = 0; - } + } }, [State.Syncing]); } @@ -487,35 +487,35 @@ var UI = (function() { self.syncInterval = setInterval(scroll, 100); //scroll(); } - }); + }); self.changed = false; self.lastEditChange = 0; - self.editor.addListener("change", function (ev) { + self.editor.addListener("change", function (ev) { self.changed = true; self.lastEditChange = ev.timeStamp || Date.now(); self.lastActivity = self.lastEditChange; }); - self.editor.addListener("keydown", function (ev) { - self.lastActivity = ev.timeStamp || Date.now(); + self.editor.addListener("keydown", function (ev) { + self.lastActivity = ev.timeStamp || Date.now(); if (self.stale || self.changed) self.lastEditChange = self.lastActivity; // so delayed refresh keeps being delayed even on cursor keys. }); - - self.editor.addCommand({ key: 'Alt-Q' }, function(ev) { + + self.editor.addCommand({ key: 'Alt-Q' }, function(ev) { self.anonEvent( function() { self.onFormatPara(ev); }, [State.Syncing] ); }); - self.editor.addListener("keydown", function (ev) { + self.editor.addListener("keydown", function (ev) { if (ev.key === "Enter" && !ev.altKey && !ev.shiftKey && !ev.metaKey && !ev.ctrlKey) { var line = self.editor.getModel().getLineContent(self.editor.getPosition().lineNumber); if (rxTable.test(line)) { ev.stopPropagation(); ev.preventDefault(); - self.addTableRow(); + self.addTableRow(); } } }); - - + + // Key bindings bindKey( "Alt-S", function() { self.synchronize(); } ); @@ -530,15 +530,15 @@ var UI = (function() { bindKey( "Alt-H", function() { self.generateHtml(); } ); bindKey( "Alt-L", function() { self.generatePdf(); } ); bindKey( "Alt-Z", function() { self.generateTexZip(); } ); - bindKey( { key: "Alt-A", stop: true }, function(ev) { + bindKey( { key: "Alt-A", stop: true }, function(ev) { if (self.spellCheckMenu && self.spellCheckMenu.isVisible()) { self.spellCheckMenu.menu.ignore(ev); self.spellCheckMenu.hide(); } }); for(var i = 1; i <= 8; i++) { - (function(idx) { - bindKey( "Alt-" + idx.toString(), function(ev) { + (function(idx) { + bindKey( "Alt-" + idx.toString(), function(ev) { if (self.spellCheckMenu && self.spellCheckMenu.isVisible()) { ev.preventDefault(); ev.stopPropagation(); @@ -561,7 +561,7 @@ var UI = (function() { if (Util.hasClassName(elem,"save-link")) { ev.cancelBubble = true; - var path = decodeURIComponent(elem.getAttribute("data-path")); + var path = decodeURIComponent(elem.getAttribute("data-path")); var mime = decodeURIComponent(elem.getAttribute("data-mime")); if (path) { self.saveUserContent(path,mime); @@ -574,7 +574,7 @@ var UI = (function() { // ---- - + document.getElementById("sync-now").onclick = function(ev) { self.synchronize(); }; @@ -593,9 +593,9 @@ var UI = (function() { var delta = now - self.lastMouseUp; self.lastMouseUp = now; if (delta <= 200) { // check for double click - self.anonEvent( function() { + self.anonEvent( function() { if (ev.target.type === 6) { // on text - var lineNo = ev.target.position.lineNumber; + var lineNo = ev.target.position.lineNumber; var line = self.editor.getModel().getLineContent(lineNo); // match include? var cap = /^\s*\[\s*INCLUDE\s*=["']?([^"'\]\n\r\t:]+)["']?\s*(:\s*\w+\s*)?\]\s*$/.exec(line) @@ -614,7 +614,7 @@ var UI = (function() { if (cap1 && cap2) { var matched = cap1[0] + cap2[0]; if (matched && matched.length > 0 && self.storage && self.storage.existsLocal(matched)) { - self.editFile(matched); + self.editFile(matched); } } } @@ -637,7 +637,7 @@ var UI = (function() { self.insertFiles(files,pos); } else if ((ev.target.type === 4 /* line-decorations */ || ev.target.type === 2 /* glyph_margin */ ) - && ev.target.position && ev.target.element) + && ev.target.position && ev.target.element) { var msg = self.getDecorationMessage(self.editName,ev.target.position.lineNumber, ev.target.type===2); if (msg) { @@ -648,9 +648,9 @@ var UI = (function() { }); self.editor.addListener("") - + self.editorPane = document.getElementById("editor"); - self.editorPane.addEventListener("drop", function(ev) { + self.editorPane.addEventListener("drop", function(ev) { ev.stopPropagation(); ev.preventDefault(); self.anonEvent( function() { @@ -660,8 +660,8 @@ var UI = (function() { viewLine = viewLine.parentNode; } if (viewLine) { - var editView = self.editor.getView(); - var lines = editView.viewLines; + var editView = self.editor.getView(); + var lines = editView.viewLines; var posLine = -1; for(var i = 0; i < lines._lines.length; i++) { if (lines._lines[i]._domNode === viewLine) { @@ -683,27 +683,27 @@ var UI = (function() { ev.preventDefault(); ev.dataTransfer.dropEffect = "copy"; }, false); - - self.spellCheckMenu = CustomHover.create("spellcheck.content.hover.menu",self.editor, - new SpellCheck.SpellCheckMenu(self.spellChecker, + + self.spellCheckMenu = CustomHover.create("spellcheck.content.hover.menu",self.editor, + new SpellCheck.SpellCheckMenu(self.spellChecker, function(id) { return self.findDecorationById(id); }, function(range,replacement) { var command = new ReplaceCommand.ReplaceCommand( range, replacement ); self.editor.executeCommand("madoko",command); - }, + }, function(id,tag) { self.removeDecorationsOn(id,tag); - }, + }, function(pos) { self.gotoNextError(pos); } ) ); - self.errorMenu = CustomHover.create("error.glyph.hover.menu",self.editor, - new ErrorMenu.ErrorMenu( + self.errorMenu = CustomHover.create("error.glyph.hover.menu",self.editor, + new ErrorMenu.ErrorMenu( function(pos) { self.gotoNextError(pos); } @@ -714,20 +714,20 @@ var UI = (function() { // synchronize on cursor position changes // disabled for now, scroll events seem to be enough /* - self.editor.addListener("positionChanged", function (e) { + self.editor.addListener("positionChanged", function (e) { self.syncView(); }); */ - + // listen to preview load messages window.addEventListener("message", function(ev) { self.anonEvent( function() { // check origin and source so no-one but our view can send messages if ((ev.origin !== "null" && ev.origin !== origin) || typeof ev.data !== "string") return; - if (ev.source !== self.view.contentWindow) return; + if (ev.source !== self.view.contentWindow) return; // console.log("preview event: " + ev.data); var info = JSON.parse(ev.data); - if (!info || !info.eventType) return; + if (!info || !info.eventType) return; if (info.eventType === "previewContentLoaded") { return self.viewLoaded(); } @@ -753,7 +753,7 @@ var UI = (function() { self.iconDisconnect = document.getElementById("icon-disconnect"); self.lastVersionCheck = 0; - // request cached version; so it corresponds to the cache-manifest version + // request cached version; so it corresponds to the cache-manifest version self.version = null; Util.getAppVersionInfoFull().then( function(version) { if (version) { @@ -761,7 +761,7 @@ var UI = (function() { var shortDigest = "(" + self.version.digest.substr(0,6) + ")"; var shortDate = self.version.date.substr(0,10); var elem = document.getElementById("madokoWebVersion"); - if (elem) elem.textContent = self.version.version || "?"; + if (elem) elem.textContent = self.version.version || "?"; elem = document.getElementById("madokoVersion"); if (elem) elem.textContent = self.version.madokoVersion || "?"; elem = document.getElementById("madokoDigest"); @@ -769,19 +769,19 @@ var UI = (function() { elem.textContent = ", " + shortDate + " " + shortDigest; elem.setAttribute("title","digest: " + self.version.digest); } - + // check if we just updated var localVersion = Util.jsonParse(localStorage.getItem("version")); if (localVersion==null || localVersion.digest !== version.digest) { localStorage.setItem("version", JSON.stringify(version)); self.showUpdateMessage(); } - } + } }); document.getElementById("showversion").onclick = function() { self.showUpdateMessage(); }; - + var autoSync = function() { var now = Date.now(); @@ -791,19 +791,19 @@ var UI = (function() { } // update connection status and synchronize - self.updateConnectionStatus().then( function(status) { + self.updateConnectionStatus().then( function(status) { if (self.storage.remote.canSync) { if (status===0) { if (now - self.lastConUsersCheck >= 5000) { self.showConcurrentUsers( now - self.lastConUsersCheck < 30000 ); } } - + if (status===400) { Util.message("Could not synchronize because the Madoko server could not be reached (offline?)", Util.Msg.Info); } else { // force login if not connected - if (self.settings.autoSync && self.state === State.Normal) { + if (self.settings.autoSync && self.state === State.Normal) { if (self.lastSync === 0 || (now - self.lastSync >= 30000 && now - self.lastEditChange > 5000)) { self.lastSync = Date.now(); // set last sync so we won't popup too many dialogs.. self.synchronize(self.storage.remote.canCommit); // pull only? @@ -813,42 +813,42 @@ var UI = (function() { } }); - // check if an app update happened + // check if an app update happened if (self.state === State.Normal && self.appUpdateReady) { - self.appUpdateReady = false; - Util.message("Madoko has been updated. Please reload.", Util.Msg.Status); + self.appUpdateReady = false; + Util.message("Madoko has been updated. Please reload.", Util.Msg.Status); self.reload(true); } // check the version number on the server every minute - if (now - self.lastVersionCheck >= 60000) { + if (now - self.lastVersionCheck >= 60000) { self.lastVersionCheck = now; // first post stats self.postStat(); - // request lastest appversion from the server + // request lastest appversion from the server Util.getAppVersionInfo(true).then( function(version) { if (!version) return; if (self.appUpdateReady || !self.version) return; if (self.version.digest === version.digest) return; if (self.version.updateDigest === version.digest) { // are we updating right now to this version? - // firefox doesn't reliably send a update ready event, check here also. + // firefox doesn't reliably send a update ready event, check here also. if (window.applicationCache.status === window.applicationCache.UPDATEREADY) //|| window.applicationCache.status === window.applicationCache.IDLE) // this is for Firefox which doesn't update the status correctly { window.applicationCache.swapCache(); - self.appUpdateReady = true; + self.appUpdateReady = true; } else if (isFirefox && window.applicationCache.status === window.applicationCache.IDLE) { - self.version.digest = version.digest; // prevent further alerts + self.version.digest = version.digest; // prevent further alerts alert("Madoko has updated but Firefox has a bug (780197) preventing it to update automatically." + "\nClear your history (in particular the 'Offline website data') -- and reload." + "\n\nA quick way to clear the Madoko application cache is to press 'Shift+F2' and" + "\nissue the command 'appcache clear' (and reload after that)"); } } - else { + else { self.version.updateDigest = version.digest; // remember we update to this version - window.applicationCache.update(); // update the cache -- will trigger a reload later on. + window.applicationCache.update(); // update the cache -- will trigger a reload later on. Util.message("Downloading updates...", Util.Msg.Status); } }); @@ -870,10 +870,10 @@ var UI = (function() { var openEvent = function(ev) { self.event( "loaded", "loading...", State.Loading, function() { - return Storage.openFile(self.storage).then( function(res) { + return Storage.openFile(self.storage).then( function(res) { return self.updateConnectionStatus().then( function() { if (!res) return Promise.resolved(); // canceled - return self.openFile(res.storage,res.docName); + return self.openFile(res.storage,res.docName); }); }, function(err) { self.updateConnectionStatus(); @@ -885,35 +885,35 @@ var UI = (function() { document.getElementById("import-tex").onclick = function(ev) { self.event( "imported", "importing...", State.Loading, function() { - return self.importTex().always( function() { + return self.importTex().always( function() { return self.updateConnectionStatus(); }); }); }; - + document.getElementById("signin").onclick = function(ev) { - if (self.storage && self.storage.remote.needSignin) { + if (self.storage && self.storage.remote.needSignin) { return self.anonEvent( function() { return self.login(" "); - }); + }); } }; - + document.getElementById("signout").onclick = function(ev) { - if (self.storage && self.storage.remote.needSignin) { + if (self.storage && self.storage.remote.needSignin) { return self.anonEvent( function() { return self.storage.remote.logout(true).then( function() { return self.updateConnectionStatus(); }); - }); + }); } }; - + var newEvent = function(ev) { self.event( "created", "creating...", State.Loading, function() { - return Storage.createFile(self.storage).then( function(res) { + return Storage.createFile(self.storage).then( function(res) { if (!res) return Promise.resolved(); // canceled - return self.openFile(res.storage,res.docName); + return self.openFile(res.storage,res.docName); }); }); }; @@ -928,27 +928,27 @@ var UI = (function() { return self.saveTo(); }); } - + document.getElementById("export-html").onclick = function(ev) { - self.generateHtml(); + self.generateHtml(); } document.getElementById("azure").onclick = function(ev) { - self.generateSite(); + self.generateSite(); } document.getElementById("export-pdf").onclick = function(ev) { - return self.generatePdf(); + return self.generatePdf(); } document.getElementById("export-texzip").onclick = function(ev) { - return self.generateTexZip(); + return self.generateTexZip(); } document.getElementById("snapshot").onclick = function(ev) { - self.event( "Snapshot created", "saving snapshot...", State.Syncing, function() { + self.event( "Snapshot created", "saving snapshot...", State.Syncing, function() { return self.withSyncSpinner( function() { - return self.storage.createSnapshot(self.docName); + return self.storage.createSnapshot(self.docName); }); }); } @@ -957,14 +957,14 @@ var UI = (function() { self.anonEvent( function() { self.editSelect(); }, [State.Syncing]); - }; - + }; + document.getElementById("edit-select-files").onclick = function(ev) { self.anonEvent( function() { var elem = ev.target; while(elem && elem.nodeName !== "DIV") { if (elem.nodeName === "A") return; //don't proceed if clicking on explicit link - elem = elem.parentNode; + elem = elem.parentNode; } if (elem && elem.getAttribute) { // IE10 doesn't support data-set so we use getAttribute var path = decodeURIComponent(elem.getAttribute("data-file")); @@ -988,7 +988,7 @@ var UI = (function() { return self.saveUserContent( path, mime ); } else { - return self.editFile(path); + return self.editFile(path); } }, [State.Syncing,State.Exporting]); } @@ -1001,7 +1001,7 @@ var UI = (function() { self.anonEvent( function() { var elem = ev.target; while(elem && elem.nodeName !== "DIV") { - elem = elem.parentNode; + elem = elem.parentNode; } if (!elem) return; var epath = elem.getAttribute("data-path"); @@ -1060,8 +1060,8 @@ var UI = (function() { document.getElementById("console-out").ondblclick = messageDblClick; document.getElementById("status").ondblclick = messageDblClick; - - self.syncer.onclick = function(ev) { + + self.syncer.onclick = function(ev) { self.synchronize(); } @@ -1071,10 +1071,10 @@ var UI = (function() { }); } - + // narrow and wide editor panes - //viewpane.addEventListener('transitionend', function( event ) { - // self.syncView(); + //viewpane.addEventListener('transitionend', function( event ) { + // self.syncView(); //}, false); function toggleFullView() { @@ -1098,11 +1098,11 @@ var UI = (function() { document.getElementById("close-fullview").onclick = function(ev) { closeFullView(); } - + document.getElementById("view-full").onclick = function(ev) { toggleFullView(); } - + // font size document.getElementById("font-small").onclick = function(ev) { self.updateSettings ({fontScale:"small"}); @@ -1116,7 +1116,7 @@ var UI = (function() { document.getElementById("font-x-large").onclick = function(ev) { self.updateSettings({fontScale:"x-large"}); } - + // Theme document.getElementById("theme-ivory").onclick = function(ev) { self.updateSettings({theme:"ivory"}) @@ -1132,8 +1132,8 @@ var UI = (function() { self.initTools(); // emulate hovering by clicks for touch devices - Util.enablePopupClickHovering(); - + Util.enablePopupClickHovering(); + // pinned menus var pin = Util.enablePinned(); @@ -1166,10 +1166,10 @@ var UI = (function() { } else if (isConnected) { Util.removeClassName(self.app,"disconnected"); - Util.addClassName(self.app,"connected"); + Util.addClassName(self.app,"connected"); } else { - Util.removeClassName(self.app,"connected"); + Util.removeClassName(self.app,"connected"); Util.addClassName(self.app,"disconnected"); } @@ -1177,7 +1177,7 @@ var UI = (function() { self.connectionMessage.textContent = stg.remote.displayName; self.connectionMessage.title = stg.remote.title; } - + var inviteUrl = ""; if (stg && stg.remote) { var remoteLogo = "images/dark/" + stg.remote.logo; @@ -1190,7 +1190,7 @@ var UI = (function() { document.getElementById("connection-content").setAttribute("title", "As " + userName); }); } - */ + */ } document.getElementById("invite-link").href = inviteUrl; } @@ -1200,7 +1200,7 @@ var UI = (function() { if (!stg) stg = self.storage; if (!stg) return Promise.resolved(false); return stg.connect().then( function(status) { - self.isConnected = (status === 0); + self.isConnected = (status === 0); self.updateRemoteLogo(stg,self.isConnected); return status; }); @@ -1208,19 +1208,19 @@ var UI = (function() { UI.prototype.setEditText = function( text, options, mode ) { var self = this; - self.editor.editFile(self.editName,text,options,mode) + self.editor.editFile(self.editName,text,options,mode) self.lastSpellCheck = 0; } - UI.prototype.getEditText = function() { + UI.prototype.getEditText = function() { var self = this; - return self.editor.getValue(); + return self.editor.getValue(); } UI.prototype.setStale = function() { var self = this; self.stale = true; - if (self.asyncMadoko) self.asyncMadoko.setStale(); + if (self.asyncMadoko) self.asyncMadoko.setStale(); } function findSpan( text, line0, col0, line1, col1 ) { @@ -1261,7 +1261,7 @@ var UI = (function() { else { var s = text1.substr(end1); end0 = text0.indexOf(s,i); - if (end0 < 0) return null; + if (end0 < 0) return null; } while( end0 > i ) { if (text0[end0] !== text1[end1]) break; @@ -1309,7 +1309,7 @@ var UI = (function() { function findTextNode( elem, text ) { if (!elem || !text) return null; if (elem.nodeType===3) { - if (elem.textContent === text) return elem; + if (elem.textContent === text) return elem; } else { for( var child = elem.firstChild; child != null; child = child.nextSibling) { @@ -1317,12 +1317,12 @@ var UI = (function() { if (res) return res; } } - return null; + return null; } UI.prototype.viewLoaded = function() { var self = this; - // self.syncView({ force: true }); + // self.syncView({ force: true }); } UI.prototype.viewHTML = function( html, time0 ) { @@ -1334,8 +1334,8 @@ var UI = (function() { self.stat.editTotal = self.stat.editTotal + (self.lastEditChange - self.stat.editLast); } self.stat.editLast = self.lastEditChange; - - + + function updateFull() { update(); // for separate viewer @@ -1355,7 +1355,7 @@ var UI = (function() { return false; } - if (self.html0) { + if (self.html0) { var dif = simpleDiff(self.html0,html); if (!dif || /[<>"]/.test(dif.text)) return updateFull(); var newSpan = { pos0: dif.start, pos1: dif.end1, text: dif.text1 }; @@ -1365,12 +1365,12 @@ var UI = (function() { var i = self.html0.indexOf(oldSpan.text); if (i !== oldSpan.pos0) return updateFull(); // ok, we can identify a unique text node in the html - update(oldSpan.textContent,newSpan.textContent); + update(oldSpan.textContent,newSpan.textContent); return true; } else { return updateFull(); - } + } } function stripMarkup(s) { @@ -1399,8 +1399,8 @@ var UI = (function() { // clear self.citations = null; if (self.editor) self.editor.setSuggestCitations([]); - menu.innerHTML = noCitations; - return; + menu.innerHTML = noCitations; + return; } // parse citations from this bib file @@ -1426,7 +1426,7 @@ var UI = (function() { cites = cites.sort(function(c1,c2) { var s1 = c1.name.toLowerCase(); var s2 = c2.name.toLowerCase(); - return (s1 < s2 ? -1 : (s1 > s2 ? 1 : 0)); + return (s1 < s2 ? -1 : (s1 > s2 ? 1 : 0)); }); if (self.editor) self.editor.setSuggestCitations(cites); @@ -1447,13 +1447,13 @@ var UI = (function() { var json = "["+ txt.split("\n").filter(function(line){ return (line.length>0); }).join(",\n") + "]"; var items = Util.jsonParse(json,[]); var itemMap = new Map(); - items.forEach( function(item) { + items.forEach( function(item) { var key = item[keyName]; if (!itemMap.contains(key)) itemMap.set( key, item ); }); return itemMap.sortedKeyElems().map( function(kv) { return kv.value; }); } - + var customSnippets = new Map([ { key: "equation", value: "Equation { #eq-{{name}} }" }, { key: "figure", value: "Figure { #fig-{{name}} caption=\"{{caption}}\" }\n{{content}}" }, @@ -1483,7 +1483,7 @@ var UI = (function() { // parse labels var cites = new Map(); - var labels = jsonParseLineArray(labelsTxt).filter( function(item) { + var labels = jsonParseLineArray(labelsTxt).filter( function(item) { if (Util.startsWith(item.name, "fn-")) { return false; } @@ -1498,7 +1498,7 @@ var UI = (function() { label.title = stripMarkup(label.caption || label.text); return label; }); - + // update suggestions if (self.editor) self.editor.setSuggestLabels(labels); @@ -1518,7 +1518,7 @@ var UI = (function() { }); if (self.editor) self.editor.setSuggestLinks(links); - + // render labels var menuLabels = document.getElementById("tool-reference-content"); @@ -1541,7 +1541,7 @@ var UI = (function() { if (elem.spinDelay == null) elem.spinDelay = self.refreshRate * 2; if (elem.spinners < 0) elem.spinners = 0; - if (enable && elem.spinners === 0) { + if (enable && elem.spinners === 0) { setTimeout( function() { if (elem.spinners >= 1) Util.addClassName(elem,"spin"); }, elem.spinDelay ); @@ -1563,7 +1563,7 @@ var UI = (function() { self.showSpinner(enable); } - self.asyncMadoko = new Util.AsyncRunner( self.refreshRate, showSpinner, + self.asyncMadoko = new Util.AsyncRunner( self.refreshRate, showSpinner, function() { var changed = self.changed; self.changed = false; @@ -1588,12 +1588,12 @@ var UI = (function() { self.docText = self.getEditText(); } return self.runner.runMadoko(self.docText, { - docname: self.docName, - round: round, + docname: self.docName, + round: round, time0: Date.now(), showErrors: function(errs) { self.showErrors(errs,false,"warning"); } }).then( function(res) { - self.htmlText = res.content; + self.htmlText = res.content; self.fileOrder = res.fileOrder || []; // used for gotoNextError var quick = self.viewHTML(res.content, res.ctx.time0); self.updateLabels(res.labels,res.links,res.customs,res.entities); @@ -1606,9 +1606,9 @@ var UI = (function() { if ((res.mathPlainDoc && self.lastMathPlainDoc !== res.mathPlainDoc) || (res.mathFullDoc && self.lastMathFullDoc !== res.mathFullDoc)) { // prevents infinite math rerun on latex error self.asyncServer.setStale(); - } + } if (res.mathPlainDoc) self.lastMathPlainDoc = res.mathPlainDoc; - if (res.mathFullDoc) self.lastMathFullDoc = res.mathFullDoc; + if (res.mathFullDoc) self.lastMathFullDoc = res.mathFullDoc; } else { self.asyncServer.setStale(); @@ -1621,14 +1621,14 @@ var UI = (function() { if (!res.runAgain && self.syncOnRender) { self.syncOnRender = false; self.syncView({force:true}); - } + } self.removeDecorations(false,"merge"); self.showConcurrentUsers( true ); // adjust delayed view? self.lastRenderWasSlow = (res.avgTime > 400); - - + + /* // adjust refresh rate dynamically if (res.avgTime > 1000 && self.refreshRate < 1000) { @@ -1640,7 +1640,7 @@ var UI = (function() { self.asyncMadoko.resume(self.refreshRate); } */ - + /* // adjust delayed view update automatically if (res.avgTime > 300) { @@ -1652,46 +1652,46 @@ var UI = (function() { self.checkDelayedUpdate.checked = false; } */ - - return ("update: " + res.ctx.round + - (quick ? " (quick view update)" : "") + + + return ("update: " + res.ctx.round + + (quick ? " (quick view update)" : "") + (!self.settings.delayedUpdate ? " (continuous)" : "") + //"\n refresh rate: " + self.refreshRate.toFixed(0) + "ms" + ", avg. time: " + res.avgTime.toFixed(0) + "ms" + - " (" + (self.lastRenderWasSlow ? "slow" : (self.lastViewRenderWasSlow ? "slow-view" : "quick")) + ")"); + " (" + (self.lastRenderWasSlow ? "slow" : (self.lastViewRenderWasSlow ? "slow-view" : "quick")) + ")"); }, function(err) { - self.onError(err); + self.onError(err); } ); } ); - self.asyncServer = new Util.AsyncRunner( self.serverRefreshRate, function(enable) { self.showSpinner(enable, self.exportSpinner) }, + self.asyncServer = new Util.AsyncRunner( self.serverRefreshRate, function(enable) { self.showSpinner(enable, self.exportSpinner) }, function() { return false; }, function(round) { var ctx = { - docname: self.docName, + docname: self.docName, round:round, disableServer: self.settings.disableServer, statusMessage: "Rendering math and references", showErrors: function(errs) { self.showErrors(errs,false); }, }; - return self.runner.runMadokoServer(self.docText, ctx ).then( + return self.runner.runMadokoServer(self.docText, ctx ).then( function(ctx) { // self.asyncServer.clearStale(); // stale is usually set by intermediate madoko runs // run madoko locally again using our generated files (and force a run) return self.asyncMadoko.run(true); }, function(err) { - self.onError(err); + self.onError(err); } - ); + ); } ); self.lastSpellCheck = 0; - self.asyncSpellCheck = new Util.AsyncRunner( 2000, null, + self.asyncSpellCheck = new Util.AsyncRunner( 2000, null, function() { var now = Date.now(); return (self.settings.spellCheck && self.lastEditChange > self.lastSpellCheck && ((now - self.lastEditChange) > 1000)); @@ -1726,7 +1726,7 @@ var UI = (function() { var pos = self.editor.getPosition(); var text = self.getEditText(); self.editContent = text; - if (self.storage) self.storage.writeFile( self.editName, text, { position: pos } ); // todo: not for readOnly + if (self.storage) self.storage.writeFile( self.editName, text, { position: pos } ); // todo: not for readOnly } // synchronous save state to local disk @@ -1734,24 +1734,24 @@ var UI = (function() { var self = this; self.saveSettings(); if (!self.storage || !self.editName) return {}; - - self.flush(); - var pos = self.editor.getPosition(); - var doc = { - docName: self.docName, - editName: self.editName, + + self.flush(); + var pos = self.editor.getPosition(); + var doc = { + docName: self.docName, + editName: self.editName, pos: pos, - storage: self.storage.persist(self.tabDb.limit()), + storage: self.storage.persist(self.tabDb.limit()), }; try { window.tabStorage.setItem( "document", doc ); - window.tabStorage.setItem( "editContent", self.editContent ); // updated by flush + window.tabStorage.setItem( "editContent", self.editContent ); // updated by flush return doc; } catch(exn) { Util.message("Unable to save document state to local storage: " + exn.toString(), Util.Msg.Warning); return doc; - }; + }; } // Asynchonous full save @@ -1794,7 +1794,7 @@ var UI = (function() { var fname = (files && files[0]) ? files[0].name : null; if (!fname || Util.extname(fname) !== ".tex") throw new Error("Sorry, can only import .tex files."); var docName = Storage.sanitizeFileName(Util.stemname(fname)) + ".mdk"; - var stg = Storage.createNullStorage(); + var stg = Storage.createNullStorage(); stg.writeFile(docName,"") return self.setStorage(stg,docName).then( function() { self.insertFiles(files); @@ -1813,7 +1813,7 @@ var UI = (function() { if (!yes) throw new Error("operation cancelled"); return Storage.httpOpenFile(url,doc); }).then( function(res) { - return self.openFile(res.storage,res.docName); + return self.openFile(res.storage,res.docName); }).then( function() { return true; }, function(err) { @@ -1839,12 +1839,12 @@ var UI = (function() { } return self.withSyncSpinner( function() { return stg.readFile(docName, false); - }).then( function(file) { + }).then( function(file) { if (self.storage) { self.storage.destroy(); // clears all event listeners self.updateCitations(null); // clears citations self.updateLabels(null,null,null,null); // clears references - self.dispatchViewEvent({eventType: "reload"}); + self.dispatchViewEvent({eventType: "reload"}); //self.viewHTML( "<p>Rendering...</p>", Date.now() ); //self.storage.clearEventListener(self); } @@ -1860,7 +1860,7 @@ var UI = (function() { self.previewFileUpdate(file); } }); - + self.storage.addEventListener("update",self); //self.storage.addEventListener("delete",self); self.runner.setStorage(self.storage); @@ -1870,13 +1870,13 @@ var UI = (function() { var remoteType = self.storage.remote.type; var remoteMsg = (remoteType==="local" ? "browser local" : remoteType); self.remoteLogo.src = "images/dark/" + remoteLogo; - self.remoteLogo.title = "Connected to " + remoteMsg + " storage"; + self.remoteLogo.title = "Connected to " + remoteMsg + " storage"; */ self.editor.clearEditState(); self.editName = ""; - return self.editFile(self.docName).always( function() { self.setStale(); } ).then( function() { + return self.editFile(self.docName).always( function() { self.setStale(); } ).then( function() { return self.updateConnectionStatus().then( function() { - return fresh; + return fresh; }); }); }); @@ -1886,7 +1886,7 @@ var UI = (function() { UI.prototype.initializeStorage = function(stg,docName,cont) { var self = this; - docName = docName || "document.mdk"; + docName = docName || "document.mdk"; var cap = /[#&]template=([^=&#;]+)/.exec(window.location.hash); if (cap) window.location.hash = ""; if (cap || !stg) { @@ -1900,7 +1900,7 @@ var UI = (function() { return cont(stg,docName,true); }); }); - } + } else { return cont(stg,docName,false); } @@ -1927,15 +1927,15 @@ var UI = (function() { if (self.editName) { self.flush(self.editName); } - //self.state = State.Loading; - if (fpath===self.editName) loadEditor = Promise.resolved(null) - else loadEditor = self.spinWhile(self.syncer, self.storage.readFile(fpath, false)).then( function(file) { + //self.state = State.Loading; + if (fpath===self.editName) loadEditor = Promise.resolved(null) + else loadEditor = self.spinWhile(self.syncer, self.storage.readFile(fpath, false)).then( function(file) { self.hideDecorations(); self.showConcurrentUsers(false,"none"); if (self.editName === self.docName) { self.docText = self.getEditText(); } - + var options = { readOnly: !Storage.isEditable(file), theme: self.getCurrentTheme(), @@ -1949,7 +1949,7 @@ var UI = (function() { self.onFileUpdate(file); // update display etc. return Storage.getEditPosition(file); }); - return loadEditor.then( function(posx) { + return loadEditor.then( function(posx) { if (!pos) pos = posx; if (pos) { self.gotoPosition(pos, reveal ); @@ -1959,10 +1959,10 @@ var UI = (function() { self.localFullSave(); // self.syncView({force:true}) // not yet rendered so cannot sync just yet self.syncOnRender = true; - }); - // .always( function() { - // self.state = State.Normal; - // }); + }); + // .always( function() { + // self.state = State.Normal; + // }); } UI.prototype.onContentChanged = function(ev) { @@ -1973,9 +1973,9 @@ var UI = (function() { var self = this; self.editor.setPosition(pos,true,true); if (reveal) { - self.editor.revealPosition( pos, true, true ); + self.editor.revealPosition( pos, true, true ); - } + } } @@ -1999,7 +1999,7 @@ var UI = (function() { if (!TabStorage.claim(tabNo)) throw new Error("Cannot open document: it is already opened in another tab"); var obj = window.tabStorage.getItemFrom(tabNo,"document"); if (obj==null || obj.storage == null) return self.setStorage(null,null); - + // read needed files return Promise.map( obj.storage.files, function(fname) { var key = "/" + fname; @@ -2028,10 +2028,10 @@ var UI = (function() { UI.prototype.checkSynced = function() { var self = this; - return Promise.do( function() { - if (self.storage) + return Promise.do( function() { + if (self.storage) return self.storage; - else + else return self.localLoad().then( function() { return self.localStorage; }); }).then( function(stg) { if (stg && !stg.isSynced()) { @@ -2048,7 +2048,7 @@ var UI = (function() { UI.prototype.openFile = function(storage,fname) { var self = this; var mime = Util.mimeFromExt(fname); - if (fname && !(mime === "text/madoko" || mime==="text/markdown") ) return Util.message("only markdown (.mdk) files can be selected",Util.Msg.Error); + if (fname && !(mime === "text/madoko" || mime==="text/markdown") ) return Util.message("only markdown (.mdk) files can be selected",Util.Msg.Error); return self.setStorage( storage, fname ); } @@ -2056,7 +2056,7 @@ var UI = (function() { UI.prototype.displayFile = function(file,extensive) { var self = this; var disable = (Storage.isEditable(file) ? "" : " disable"); - var sym = // ((self.storage.remote.canCommit && file.sha==null) ? "<span title='Changes not yet committed'>&#x2217;</span>" : "") + + var sym = // ((self.storage.remote.canCommit && file.sha==null) ? "<span title='Changes not yet committed'>&#x2217;</span>" : "") + (file.modified || (self.storage.remote.canCommit && file.sha===null) ? "<span title='Changes not yet synchronized'>&#9679;</span>" : ""); var icon = "<span class='file-status'>" + sym + "</span>"; var span = "<span class='file " + file.mime.replace(/[^\w]+/g,"-") + disable + "'" + @@ -2064,7 +2064,7 @@ var UI = (function() { Util.escape(file.path) + icon + "</span>"; var extra = ""; if (extensive) { - var len = file.content.length; + var len = file.content.length; if (Storage.isEditable(file)) { var matches = file.content.replace(/<!--[\s\S]*?-->/,"").match(/[^\d\s~`!@#$%^&\*\(\)\[\]\{\}\|\\\/<>,\.\+=:;'"\?]+/g); var words = matches ? matches.length : 0; @@ -2110,10 +2110,10 @@ var UI = (function() { var hide = ""; // (Util.extname(file.path) === ".dimx" ? " hide" : ""); var nosync = (file.nosync ? " nosync" : ""); var line = "<div data-file='" + encodeURIComponent(file.path) + "' " + - "class='button file hoverbox" + disable + main + hide + nosync + "'>" + + "class='button file hoverbox" + disable + main + hide + nosync + "'>" + self.displayFile(file,true) + "</div>"; - var info = { line: line, path: file.path } - if (Util.startsWith(file.mime,"image/")) images.push(info); + var info = { line: line, path: file.path } + if (Util.startsWith(file.mime,"image/")) images.push(info); else if (!disable) files.push(info); else if (Util.stemname(self.docName) === Util.stemname(file.path) && (ext===".pdf" || ext===".html" || ext===".zip")) finals.push(info); else if (file.nosync) nosyncs.push(info); @@ -2121,11 +2121,11 @@ var UI = (function() { } }); }; - + /* var dir = document.getElementById("edit-select-directory"); if (dir) { - dir.innerHTML = "<img src='images/" + self.storage.remote.logo + "'/> " + + dir.innerHTML = "<img src='images/" + self.storage.remote.logo + "'/> " + Util.escape( self.storage.folder() ) + "<hr/>"; } */ @@ -2149,13 +2149,13 @@ var UI = (function() { (divname ? "\n</div>" : ""); } - div.innerHTML = + div.innerHTML = joinLines(finals,"rendered","rendered") + - joinLines(files, "files", "files") + - "<div class='binaries'>" + - joinLines(images,"images","images") + - joinLines(generated, "generated","generated") + - joinLines(nosyncs, "nosyncs","server provided") + + joinLines(files, "files", "files") + + "<div class='binaries'>" + + joinLines(images,"images","images") + + joinLines(generated, "generated","generated") + + joinLines(nosyncs, "nosyncs","server provided") + "</div>"; } @@ -2169,7 +2169,7 @@ var UI = (function() { if (!self.storage.remote.canSync || self.settings.disableServer) { // unconnected storage (null or http) self.usersStatus.className = ""; - return; + return; } else if (quick && (self.usersStatus.className === "" || self.usersStatus.className === "users-open" )) { // don't do a get request for a quick check return; @@ -2178,11 +2178,11 @@ var UI = (function() { self.usersStatus.className = ""; } else if (!edit) { - if (self.storage && self.storage.isModified(self.editName)) + if (self.storage && self.storage.isModified(self.editName)) edit = "write"; else if (now > self.lastActivity + 120000) // after 2 minutes of non-activity, become open which reduces get requests to server edit = "open"; - else + else edit = "read"; } var editInfo = { @@ -2197,7 +2197,7 @@ var UI = (function() { docFile = docFile + "*"; // special name for overall document files[docFile] = { kind: edit, line: 0 }; files[editFile] = { kind: edit, line: self.editor.getPosition().lineNumber }; - + self.lastConUsersCheck = Date.now(); self.storage.remote.getUserName().then( function(name) { var body = { @@ -2218,7 +2218,7 @@ var UI = (function() { var info = users.getOrCreate(user.name,user); if (user.kind==="write") { info.path = fname; - info.kind = user.kind; + info.kind = user.kind; info.line = user.line; } else if (!Util.endsWith(fileName,"*")) { @@ -2241,24 +2241,24 @@ var UI = (function() { readers = true; } status = status + "<div class='button user-" + user.kind + "' " + - "title='" + (user.kind==="write" ? "Editing document" : (user.kind==="read" ? "Viewing document" : "Opened document")) + - " " + Util.escape(Util.basename(user.path)) + - (user.line>0 ? ":" + Util.escape(user.line.toString()) : "") + + "title='" + (user.kind==="write" ? "Editing document" : (user.kind==="read" ? "Viewing document" : "Opened document")) + + " " + Util.escape(Util.basename(user.path)) + + (user.line>0 ? ":" + Util.escape(user.line.toString()) : "") + "' " + - "data-path='" + encodeURIComponent(user.path) + "' " + + "data-path='" + encodeURIComponent(user.path) + "' " + (user.line != null ? "data-line='" + encodeURIComponent(user.line.toString()) + "' " : "") + - ">" + + ">" + "<span class='icon'><img src='images/icon-user-" + user.kind + ".png'></span>" + - Util.escape(user.name) + + Util.escape(user.name) + "</div>"; }); self.usersPanel.innerHTML = status; - + if (edits.length > 0) { self.usersStatus.className = "users-write"; } else if (users.count() > 0) { - self.usersStatus.className = (readers ? "users-read" : "users-open"); + self.usersStatus.className = (readers ? "users-read" : "users-open"); } else { self.usersStatus.className = ""; @@ -2266,7 +2266,7 @@ var UI = (function() { // decorations self.showConcurrentEdits(edits); - + }); }); } @@ -2279,10 +2279,10 @@ var UI = (function() { var now = Date.now(); var body = { editTime: self.stat.editTotal, - viewTime: now - self.stat.viewStart, + viewTime: now - self.stat.viewStart, }; body.activeTime = (now < self.lastActivity + 60000 ? 60000 : 0); // any activity in the last minute? - + self.stat.editTotal = 0; self.stat.viewStart = now; @@ -2295,7 +2295,7 @@ var UI = (function() { function _saveUserContent( path, mime, content, tryOpenFirst ) { - // blob is created in our origin; + // blob is created in our origin; // so we should make sure a user can only save, not open a window in our domain // since a html page could read our local storage or do rest calls with our cookie. // (this could be problem if a user opens a document with 'evil' content) @@ -2313,7 +2313,7 @@ var UI = (function() { window.open(url,name); return; } - catch(exn) {} + catch(exn) {} } // The rest of the code handles all cases to allow saving the content locally @@ -2337,7 +2337,7 @@ var UI = (function() { } catch(exn) { // on mobile var link = self.getViewLink(path,mime); - if (link) return Util.message( { message: "Document exported", link: link }, Util.Msg.Status ); + if (link) return Util.message( { message: "Document exported", link: link }, Util.Msg.Status ); } } } @@ -2346,7 +2346,7 @@ var UI = (function() { var self = this; if (!mime) mime = Util.mimeFromExt(path); var prefix = ""; - + if (mime==="application/pdf") { var url = self.storage.getShareUrl(path); // for PDF share is best; dropbox preview is great if (!url && URL.createObjectURL && !navigator.msSaveOrOpenBlob) { // don't use for IE @@ -2358,15 +2358,15 @@ var UI = (function() { } } if (url) { - prefix = "&nbsp;(<a class='external' target='_blank' title='view in browser' href='" + Util.escape(url) + "'>view</a>)"; + prefix = "&nbsp;(<a class='external' target='_blank' title='view in browser' href='" + Util.escape(url) + "'>view</a>)"; } } // probably html, cannot use blob url directly (since a user can right-click and open in our origin) // we create fake url's that call "saveUserContent" on click return (function(msg) { - return msg + prefix + - "&nbsp;(<span class='save-link' title='download and open' data-path='" + encodeURIComponent(path) + - "' data-mime='" + encodeURIComponent(mime) + "'>open</span>)"; + return msg + prefix + + "&nbsp;(<span class='save-link' title='download and open' data-path='" + encodeURIComponent(path) + + "' data-mime='" + encodeURIComponent(mime) + "'>open</span>)"; }); } @@ -2406,22 +2406,22 @@ var UI = (function() { UI.prototype.generateOnServer = function(target,ext,msg) { var self = this; - self.event( null, msg + "...", State.Exporting, function() { + self.event( null, msg + "...", State.Exporting, function() { self.localSave(); - var ctx = { - round: 0, - docname: self.docName, - target: target, + var ctx = { + round: 0, + docname: self.docName, + target: target, includeImages: true, disableServer: self.settings.disableServer, statusMessage: msg, - showErrors: function(errs) { self.showErrors(errs,true); } + showErrors: function(errs) { self.showErrors(errs,true); } }; //if (self.settings.disableServer) { // Util.message("Cannot generate PDF because the 'disable server' menu is checked", Util.Msg.Error); // return; //} - return self.spinWhile( self.exportSpinner, + return self.spinWhile( self.exportSpinner, self.runner.runMadokoServer( self.docText, ctx ).then( function(errorCode) { if (errorCode !== 0) throw ( msg + " failed: " + ctx.message); var name = "out/" + Util.changeExt(self.docName,"." + ext); @@ -2440,12 +2440,12 @@ var UI = (function() { var self = this; return self.generateOnServer("texzip","zip","Creating LaTeX bundle"); } - + UI.prototype.generateHtml = function() { var self = this; - self.event( null, "Rendering HTML...", State.Exporting, function() { + self.event( null, "Rendering HTML...", State.Exporting, function() { self.localSave(); - return self.spinWhile( self.exportSpinner, + return self.spinWhile( self.exportSpinner, self.runner.runMadokoLocal( self.docName, self.docText, { embedLimit: 512*1024 } ).then( function(content) { var name = "out/" + Util.changeExt(self.docName,".html"); self.storage.writeFile( name, content ); @@ -2458,8 +2458,8 @@ var UI = (function() { UI.prototype.generateSite = function() { var self = this; - self.event( "Saved website", "exporting...", State.Exporting, function() { - return self.spinWhile( self.exportSpinner, + self.event( "Saved website", "exporting...", State.Exporting, function() { + return self.spinWhile( self.exportSpinner, self.runner.runMadokoLocal( self.docName, self.docText ).then( function(content) { var name = "out/" + Util.changeExt(self.docName,".html"); self.storage.writeFile( name, content ); @@ -2474,7 +2474,12 @@ var UI = (function() { Editor operations -------------------------------------------------- */ function reformatTable( lines, column ) { - var rxCell = /((?:^ *(?:\||\+(?=[:=~-])))?)((?:[^\\|+]|\\.|\+ *(?!$|[:~=\-\r\n]))+)([|]+|[\+]+(?= *[:~=\-\r\n]| *$))/g; + var rxCellCodeInline = /(?:``(?:[^`]|`(?!`))*``|`(?:[^`]|``)*`)/.source; + var rxCellTexInline = /(?:\$\{(?:[^\\\$]|\\[\s\S])+\$)/.source; + var rxCellContent = /\\./.source + "|" + rxCellTexInline + "|" + rxCellCodeInline + "|" + /[^\\|+]|\+ *(?!$|[:~=\-\r\n])/.source; + var rxCellContents = "((?:" + rxCellContent + ")+)"; + var rxCell = new RegExp(/((?:^ *(?:\||\+(?=[:=~-])))?)/.source + rxCellContents + /([|]+|[\+]+(?= *[:~=\-\r\n]| *$))/.source, "g"); + //var rxCell = /((?:^ *(?:\||\+(?=[:=~-])))?)((?:[^\\|+]|\\.|\+ *(?!$|[:~=\-\r\n]))+)([|]+|[\+]+(?= *[:~=\-\r\n]| *$))/g; var rows = lines.map( function(line) { var cells = []; var cap; @@ -2484,7 +2489,7 @@ var UI = (function() { cells.push( cap[3] ); // separator } if (!cells || cells.length===0) { - return [line,"","|"]; + return [line,"","|"]; } return cells; }); @@ -2566,8 +2571,8 @@ var UI = (function() { var col = hang0.length; var hangCol = col; text = text.substr(col); - - // split in non-breakable parts + + // split in non-breakable parts var parts = []; // text.split(/\s(?!\s*\[)/); var rxpart = /(?:[^\s\\\$\{`]|\\[\s\S]|\$(?:[^\\\$]|\\.)*\$|\{(?:[^\\\}]|\\.)*\}|(`+)(?:[^`]|(?!\1)`)*\1|\s+\[|[\\\$\{`])+/; var rxspace = /\s+/; @@ -2586,7 +2591,7 @@ var UI = (function() { text = text.substr(cap[0].length); // skip whitespace } } - } + } // and put the parts together inside the column boundaries parts.forEach( function(part) { @@ -2618,14 +2623,14 @@ var UI = (function() { var cap = /^(\s*)/.exec(line); return (cap ? cap[1].length : 0); }); - + var listCap = /^[ \t]*(([\*\+\-]|\d\.)[ \t]+)/.exec(text); var listHang = listCap ? listCap[0].length : 0; - + var hang0 = new Array(indents[0]+1).join(" "); var indent = Math.max( Math.max(indents[0],listHang), (indents.length > 1 ? Math.min.apply( null, indents.slice(1) ) : 0) ); var hang = new Array(indent+1).join(" "); - + // reformat var paraText = para.join(" "); return breakLines(paraText, column || 72, hang0, hang); @@ -2658,13 +2663,13 @@ var UI = (function() { // find table extent while ( start > 1 && rxTable.test(lines[start-2]) ) { start--; - } + } while (end < lines.length && rxTable.test(lines[end])) { end++; } var table = lines.slice(start-1,end); if (table.length <= 0) return null; - content = reformatTable(table,column); + content = reformatTable(table,column); newpos = pos; } else { @@ -2720,7 +2725,7 @@ var UI = (function() { var cap; while ((cap = reMeta.exec(text))) { text = text.substr(cap[0].length); - lineNo += Util.lineCount(cap[0]); + lineNo += Util.lineCount(cap[0]); } return lineNo; } @@ -3045,52 +3050,52 @@ var symbolsMath = [ { entity: "diams", code: 9830 }, ]; - var toolDefInclude = { + var toolDefInclude = { name: "include", title: "Include a local file", options: [ - { name : "Image", + { name : "Image", title : "Insert an image", helpLink: "#sec-image", upload : "Please select an image.", exts : [".jpg",".png",".svg",".gif",".eps"], }, - { name : "Markdown", + { name : "Markdown", title : "Include another markdown file", upload : "Please select a markdown file.", exts : [".mdk",".md",".mkdn",".markdown"], }, - { name : "Bibliography", + { name : "Bibliography", helpLink: "#sec-bib", title : "Include a BibTeX bibliography file", upload : "Please select a BibTeX bibliography file", exts : [".bib"], }, - { name : "Bibliography style (.bst)", + { name : "Bibliography style (.bst)", helpLink: "#sec-bib", title : "Use a specific bibliography style", upload : "Please select a BibTeX bibliography style file", exts : [".bst"], }, - { name : "Language colorizer", + { name : "Language colorizer", helpLink: "#syntax-highlighting", title : "Include a language syntax highlighting specification", upload : "Please select a syntax highlighting specification file", exts : [".json"], }, - { name : "CSS style", + { name : "CSS style", helpLink: "#html-keys", title : "Include a CSS style file (.css)", upload : "Please select a CSS style file.", exts : [".css"], }, - { name : "LaTeX package", + { name : "LaTeX package", helpLink: "#latex-keys", title : "Include a LaTeX package, style, or TeX file", upload : "Please select a LaTeX package file", exts : [".sty",".tex"], }, - { name : "LaTeX document class", + { name : "LaTeX document class", helpLink: "#latex-keys", title : "Include a LaTeX document class", upload : "Please select a LaTeX document class file", @@ -3114,41 +3119,41 @@ var symbolsMath = [ icon : true, title : "Inline code", }), - { name : "link", + { name : "link", icon : true, title : "Insert a link", content : "link", keys : ["Ctrl-K"], - replacer: function(txt,rng) { + replacer: function(txt,rng) { var self = this; var name = txt.replace(/[^\w\- ]+/g,"").substr(0,16); var url = "http://" + name.replace(/\s+/g,"_") + ".com"; var def = "\n[" + name + "]: " + url + " \"" + name + " title\"\n"; self.insertAfterPara(self.editor.getPosition().lineNumber, def); - return "[" + txt + "]" + (name===txt ? "" : "[" + name + "]"); + return "[" + txt + "]" + (name===txt ? "" : "[" + name + "]"); }, }, toolInline("formula","$","$",{ icon : true, title : "Inline formula", content : "e = mc^2", - keys : ["Alt-F"], - }), - toolInline("sub","~","~", { + keys : ["Alt-F"], + }), + toolInline("sub","~","~", { icon : true, title : "Sub-script", - transform: function(txt) { - return txt.replace(/~/g,"\\~").replace(/ /g,"\\ "); + transform: function(txt) { + return txt.replace(/~/g,"\\~").replace(/ /g,"\\ "); }, }), - toolInline("super","^","^", { + toolInline("super","^","^", { icon : true, title : "Super-script", - transform: function(txt) { - return txt.replace(/\^/g,"\\^").replace(/ /g,"\\ "); + transform: function(txt) { + return txt.replace(/\^/g,"\\^").replace(/ /g,"\\ "); }, }), - { name : "font", + { name : "font", icon : true, title : "Change the font family", options : [ @@ -3167,7 +3172,7 @@ var symbolsMath = [ toolCss("font-family","\"Segoe UI, sans-serif\"",""), ] }, - { name : "fontsize", + { name : "fontsize", icon : true, title : "Change the font size", options : [ @@ -3179,12 +3184,12 @@ var symbolsMath = [ toolFontSize("medium"), toolFontSize("large"), toolFontSize("x-large"), - toolFontSize("xx-large"), + toolFontSize("xx-large"), toolFontSize("initial", "The initial font size"), toolFontSize("2ex","A specific font size (%|ex|em|pt|px)"), ] - }, - { name : "color", + }, + { name : "color", icon : true, title : "Change the font color", options : [ @@ -3196,7 +3201,7 @@ var symbolsMath = [ toolColor("magenta"), toolColor("maroon"), toolColor("green"), - toolColor("navy"), + toolColor("navy"), toolColor("olive"), toolColor("teal"), toolColor("purple"), @@ -3205,10 +3210,10 @@ var symbolsMath = [ toolColor("gray"), toolColor("lightgray"), toolColor("white"), - toolColor("#335577"), + toolColor("#335577"), ] - }, - { name : "heading", + }, + { name : "heading", icon : true, title : "Insert a heading", options: [ @@ -3216,9 +3221,9 @@ var symbolsMath = [ heading("Heading 2","##"), heading("Heading 3","###"), heading("Heading 4","####"), - heading("Heading 5","#####"), + heading("Heading 5","#####"), ] - }, + }, { name: "symbol", icon: true, @@ -3272,12 +3277,12 @@ var symbolsMath = [ }, { element: "BR", }, - { name : "pre", + { name : "pre", icon : true, title : "Code block", content : "function hello() {\n return \"world\";\n}", - replacer: function(txt,rng) { - return blockRange(rng,"``` javascript" + block(txt) + "```"); + replacer: function(txt,rng) { + return blockRange(rng,"``` javascript" + block(txt) + "```"); }, }, { name : "ul", @@ -3325,43 +3330,43 @@ var symbolsMath = [ return null; } } - }, - { name : "img", + }, + { name : "img", icon : true, title : "Insert an image", content : "", upload : "Please select an image.", exts : [".jpg",".png",".svg",".gif"], - }, + }, { name : "aligncenter", icon : true, title : "Text and block alignment", options : [ - toolBlock("alignleft", { + toolBlock("alignleft", { block : "Align-Left", html : "<img src='images/icon-tool-alignleft.png'/> Left", - title : "Left align text and blocks", + title : "Left align text and blocks", helpLink: "#special-attribute-classes", content : "Left aligned text.", }), - toolBlock("aligncenter", { + toolBlock("aligncenter", { block : "Center", html : "<img src='images/icon-tool-aligncenter.png'/> Center", - title : "Center text and blocks", + title : "Center text and blocks", helpLink: "#special-attribute-classes", content : "Centered text.", }), - toolBlock("alignright", { + toolBlock("alignright", { block : "Align-Right", html : "<img src='images/icon-tool-alignright.png'/> Right", - title : "Right align text and blocks", + title : "Right align text and blocks", helpLink: "#special-attribute-classes", content : "Right aligned text.", }), - toolBlock("justify", { + toolBlock("justify", { block : "Justify", html : "<img src='images/icon-tool-justify.png'/> Justify", - title : "Justify text", + title : "Justify text", helpLink: "#sec-css", content : "Justified text.", }), @@ -3371,40 +3376,40 @@ var symbolsMath = [ icon : true, title : "Insert a figure", options : [ - toolBlock("normal", { + toolBlock("normal", { block : "Figure", html : "<img src='images/icon-tool-figurewide.png'/> Normal", - title : "Insert a regular figure", + title : "Insert a regular figure", helpLink: "#sec-figure", content : "Here is a normal figure.", attrs : "#fig-myfigure; caption:\"My caption.\"", }), - toolBlock("left", { - block : "Figure", + toolBlock("left", { + block : "Figure", html : "<img src='images/icon-tool-figure.png'/> Left", helpLink: "#sec-float", attrs : "#fig-myfigure; caption:\"My caption.\"; float:left; width:50%; margin-right:1em", content : "Here is a left figure.", - title : "Insert a left-side figure with text wrap around", + title : "Insert a left-side figure with text wrap around", }), - toolBlock("right", { - block : "Figure", + toolBlock("right", { + block : "Figure", html : "<img src='images/icon-tool-figureright.png'/> Right", helpLink: "#sec-float", content : "Here is a right figure.", attrs : "#fig-myfigure; caption:\"My caption.\"; float:right; width:50%; margin-left:1em", - title : "Insert a right-side figure with text wrap around", + title : "Insert a right-side figure with text wrap around", }), - toolBlock("wide", { - block : "Figure", + toolBlock("wide", { + block : "Figure", helpLink: "#sec-figure", - html : "<img src='images/icon-tool-figurewide2.png'/> Wide", + html : "<img src='images/icon-tool-figurewide2.png'/> Wide", attrs : "#fig-myfigure; caption:\"My caption.\"; .wide", content : "Here is a wide figure.", - title : "Insert a wide figure. In LaTeX such figure spans two columns (using the \\figure* command)", + title : "Insert a wide figure. In LaTeX such figure spans two columns (using the \\figure* command)", }), ], - }, + }, { name : "table", icon : true, title : "Table", @@ -3417,16 +3422,16 @@ var symbolsMath = [ toolTable(7), toolTable(8), ] - }, + }, { name : "undo", icon : true, - command: "undo", + command: "undo", title : "(Ctrl-Z) Undo" }, { name : "redo", icon : true, command: "redo", - title : "(Ctrl-Y) Redo" + title : "(Ctrl-Y) Redo" }, { element: "BR", }, @@ -3479,7 +3484,7 @@ var symbolsMath = [ }, customBlock("note"), customBlock("remark"), - customBlock("example"), + customBlock("example"), customBlock("abstract","", "The abstract."), customBlock("framed","","A block with a solid border."), customBlock("center","","A block with centered items."), @@ -3501,7 +3506,7 @@ var symbolsMath = [ }, customBlock("HtmlOnly","","This is only displayed in HTML","",null,"Insert markdown that is only used in HTML output"), customBlock("TexRaw","","% Raw LaTeX content","",null,"Insert raw LaTeX code (for PDF output only)"), - customBlock("TexOnly","","This is only displayed in PDF","",null,"Insert markdown that is only used in PDF output"), + customBlock("TexOnly","","This is only displayed in PDF","",null,"Insert markdown that is only used in PDF output"), ] }, { name: "math", @@ -3509,10 +3514,10 @@ var symbolsMath = [ style: "overflow-y: auto", options: [ customBlock("equation", "{ #eq-euler }","e = \\lim_{n\\to\\infty} \\left( 1 + \\frac{1}{n} \\right)^n","","#sec-math"), - customBlock("theorem", "{ #th-euler }\n(_Euler's formula_)\\", "For any real number $x$, we have: $e^{ix} = \\cos x + i \\sin x$.", "#sec-math" ), + customBlock("theorem", "{ #th-euler }\n(_Euler's formula_)\\", "For any real number $x$, we have: $e^{ix} = \\cos x + i \\sin x$.", "#sec-math" ), customBlock("proof", "", "Trivially by induction. [&box;]{float=right}" ), customBlock("lemma"), - customBlock("proposition"), + customBlock("proposition"), customBlock("corollary"), customBlock("definition"), customBlock("MathPre","","@function sqr_\\pi( num :int ) \\{\n @return (num {\\times} num \\times{} \\pi)\n\}","","#sec-mathpre","Math mode that respects whitespace and identifier names"), @@ -3538,7 +3543,7 @@ var symbolsMath = [ toolStyle("float","float:right; width:50%; margin-left:1em","left|right","Limited support in LaTeX, but works for figures"), toolStyle("line-height","=1.5em","<length>"), toolStyle("vertical-align","=middle","top|middle|bottom|baseline|<length>"), - toolStyle("display","=inline","block|inline|inline-block|hidden"), + toolStyle("display","=inline","block|inline|inline-block|hidden"), toolStyle("margin-left","=1ex"), toolStyle("margin-right","=1ex"), toolStyle("margin-top","=1ex"), @@ -3546,7 +3551,7 @@ var symbolsMath = [ toolStyle("padding-left","=1ex"), toolStyle("padding-right","=1ex"), toolStyle("padding-top","=1ex"), - toolStyle("padding-bottom","=1ex"), + toolStyle("padding-bottom","=1ex"), ] }, { name: "metadata", @@ -3583,11 +3588,11 @@ var symbolsMath = [ */ ] - }, + }, toolDefInclude, ]; - + UI.prototype.toolInsertCitation = function(txt,rng,cite) { @@ -3715,13 +3720,13 @@ var symbolsMath = [ return null; } - UI.prototype.styleReplacer = function(txt,rng,value) { + UI.prototype.styleReplacer = function(txt,rng,value) { var self = this; var cap; // anything ending with attributes: just extend cap = /^(.*)([ \t]*\}\s*)$/.exec(txt); - if (cap) { + if (cap) { return cap[1] + " " + value + cap[2]; } @@ -3730,32 +3735,32 @@ var symbolsMath = [ var res = self._styleReplaceInLine(rng,value); if (res) return res; } - + // custom block with attributes? cap = /^([ \t]*~.*?)([ \t]*\}[ \t]*$[\s\S]*)/m.exec(txt); - if (cap) { + if (cap) { return cap[1] + " " + value + cap[2]; } - // custom block + // custom block cap = /^([ ]{0,3}~.*)([\s\S]*?\r?\n[ \t]*~+\s*(?:[ \t]*End\b.*\s*)?)/i.exec(txt); - if (cap) { + if (cap) { return cap[1] + " { " + value + " }" + cap[2]; } // paragraph or list block? - if (rng.startColumn===1 && rng.startLineNumber < rng.endLineNumber) { - if (rng.endColumn===1) { + if (rng.startColumn===1 && rng.startLineNumber < rng.endLineNumber) { + if (rng.endColumn===1) { return txt + "{ " + value + " }\n"; } - var maxEndColumn = self.editor.getModel().getLineMaxColumn(rng.endLineNumber); + var maxEndColumn = self.editor.getModel().getLineMaxColumn(rng.endLineNumber); if (maxEndColumn === rng.endColumn) { return txt + "\n{ " + value + " }\n"; } } - + // default - return "[" + txt + "]{ " + value + " }"; + return "[" + txt + "]{ " + value + " }"; } function toolInline(name,pre,post,extra) { @@ -3775,7 +3780,7 @@ var symbolsMath = [ } function toolFigure(icon) { - return { + return { name : "figure", icon : icon, helpLink: "#sec-figure", @@ -3794,9 +3799,9 @@ var symbolsMath = [ title: title, replacer: function(txt,rng) { var self = this; - var lineNo = findMetaPos(self.getEditText()); + var lineNo = findMetaPos(self.getEditText()); if (lineNo > 0) { - var pos = { lineNumber: lineNo, column: 1 }; + var pos = { lineNumber: lineNo, column: 1 }; self.insertText( pad(name,12," ") + ": " + value + "\n", pos ); } return null; @@ -3818,7 +3823,7 @@ var symbolsMath = [ cols.push(i+1); } var w = 14; - var lline = pad("",w,"-"); + var lline = pad("",w,"-"); var cline = ":" + pad("",w-2,"-") + ":" var headline = "+" + lline + "|" + cline + "+" + (columns <= 2 ? "" : cols.slice(2).map( function(c) { return (c===2 ? cline : lline); } ).join("|") + "+"); var head = "|" + cols.map( function(c) { return pad( (c===2 ? " Centered " : " Heading ") + c.toString(), w, " "); } ).join("|") + "|"; @@ -3847,12 +3852,12 @@ var symbolsMath = [ } function customBlock(name,post,content,postContent,helpLink,title) { - return { + return { name: name, content: content || "Here is a " + name + ".", helpLink: helpLink, title: title, - replacer: function(txt,rng) { + replacer: function(txt,rng) { return wrapBlock(rng,"~ " + Util.capitalize(name) + (post ? " " + post : ""), txt, "~" + (rng.isEmpty() && postContent ? "\n\n" + postContent : "")); } } @@ -3871,16 +3876,16 @@ var symbolsMath = [ } function toolCustom(name,display,content,helpLink,title) { - return { + return { name: name, display: display || Util.capitalize(name), content: content || "Here is a " + name + ".", helpLink: helpLink, title: title, - replacer: function(txt,rng) { + replacer: function(txt,rng) { return wrapBlock(rng,"~ " + Util.capitalize(name), txt, "~"); } - } + } } function wrapBlock(rng,pre,txt,post) { @@ -3918,7 +3923,7 @@ var symbolsMath = [ UI.prototype.initToolKeys = function(tool,elem) { var self = this; - var handler = function() { self.toolCommand(tool); } + var handler = function() { self.toolCommand(tool); } if (tool.keys) { tool.keys.forEach( function(key) { bindKey(key,handler); @@ -3955,12 +3960,12 @@ var symbolsMath = [ if (!elem) return; var entity = decodeURIComponent(elem.getAttribute("data-entity")); if (!entity) return; - self.toolCommand( { - name: "symbol", + self.toolCommand( { + name: "symbol", replacer: function(txt,rng) { return entity; } - }); + }); }); } @@ -4008,8 +4013,8 @@ var symbolsMath = [ parent.appendChild(item); } if (tool.options || tool.symbols || tool.dynamic) { - Util.addClassName(item,"popup"); - Util.addClassName(item,tool.options ? "options":"symbols"); + Util.addClassName(item,"popup"); + Util.addClassName(item,tool.options ? "options":"symbols"); if (!tool.icon) Util.addClassName(item,"named"); var menu = document.getElementById(item.id + "-content"); if (!menu) { @@ -4019,7 +4024,7 @@ var symbolsMath = [ } Util.addClassName(menu,"menu"); Util.addClassName(menu,"boxed"); - if (tool.style) menu.setAttribute("style",tool.style); + if (tool.style) menu.setAttribute("style",tool.style); if (tool.options) { tool.options.forEach(function(subtool) { self.initTool(subtool,menu,parentName + "-" + tool.name); @@ -4033,13 +4038,13 @@ var symbolsMath = [ } } else { - item.addEventListener("click", function(ev) { + item.addEventListener("click", function(ev) { if (ev.target.nodeName !== "A") { - self.toolCommand(tool); + self.toolCommand(tool); } }); } - self.initToolKeys(tool,item); + self.initToolKeys(tool,item); } UI.prototype.initTools = function() { @@ -4075,7 +4080,7 @@ var symbolsMath = [ var cmd = self["command" + Util.capitalize(tool.command)]; if (cmd) return cmd.call(self); } - }, [State.Syncing]); + }, [State.Syncing]); } @@ -4104,7 +4109,7 @@ var symbolsMath = [ return; } - // Insert or replace some text in the document + // Insert or replace some text in the document UI.prototype.insertOrReplaceText = function( replacer, defText ) { var self = this; var select = self.editor.getSelection(); @@ -4124,16 +4129,16 @@ var symbolsMath = [ command = new ReplaceCommand.ReplaceCommandWithoutChangingPosition( select, newText ); } else { command = new Editor.ReplaceCommandWithSelection(select,newText); - } - self.editor.executeCommand("madoko",command); + } + self.editor.executeCommand("madoko",command); } } - // Insert some text in the document + // Insert some text in the document UI.prototype.insertText = function( txt, pos, moveToEnd ) { var self = this; - if (!pos) pos = self.editor.getPosition(); + if (!pos) pos = self.editor.getPosition(); var rng = new Range.Range( pos.lineNumber, pos.column, pos.lineNumber, pos.column ); var command; if (moveToEnd) @@ -4150,14 +4155,14 @@ var symbolsMath = [ var stem = Util.stemname(file.name); var name = Storage.sanitizeFileName(Util.basename(file.name)); - if (Util.isImageMime(mime)) name = "images/" + name; + if (Util.isImageMime(mime)) name = "images/" + name; if (encoding===Storage.Encoding.Base64) { var cap = /^data:([\w\/\+\-]+);(base64),([\s\S]*)$/.exec(content); if (!cap) { Util.message("invalid base64 encoding", Util.Msg.Error ); return; } - content = cap[3]; + content = cap[3]; } if (content.length >= 1.34*1024*1024) { throw new Error("file size is too large (maximum insertion size is about 1mb)"); @@ -4166,9 +4171,9 @@ var symbolsMath = [ Util.message("file size is very large; consider resizing to keep it under 512kb", Util.Msg.Warning); } - + var text = ""; - var message = "inserted"; + var message = "inserted"; if (Util.isImageMime(mime)) { var isNew = self.storage.writeFile( name, content, {encoding:encoding,mime:mime}); if (isNew) { @@ -4201,7 +4206,7 @@ var symbolsMath = [ self.setStale(); }, function(err) { Util.message("Failed to convert " + name + ": " + err.toString(), Util.Msg.Error ); - }); + }); } else { var isNew = self.storage.writeFile( name, content, {encoding:encoding,mime:mime}); @@ -4234,8 +4239,8 @@ var symbolsMath = [ Util.message( "unsupported drop file extension: " + ext, Util.Msg.Warning ); return; } - var lineNo = findMetaPos(self.getEditText()); - if (lineNo > 0) pos = { lineNumber: lineNo, column: 1 }; + var lineNo = findMetaPos(self.getEditText()); + if (lineNo > 0) pos = { lineNumber: lineNo, column: 1 }; } } if (text) { @@ -4250,15 +4255,15 @@ var symbolsMath = [ var self = this; if (!files) return; if (!pos) pos = self.editor.getPosition(); - for (var i = 0, f; f = files[i]; i++) { - var encoding = Storage.Encoding.fromExt(f.name); + for (var i = 0, f; f = files[i]; i++) { + var encoding = Storage.Encoding.fromExt(f.name); var mime = f.type || Util.mimeFromExt(f.name); if (!(Util.isImageMime(mime) || Util.isTextMime(mime))) { // only images or text.. continue; } - + var reader = new FileReader(); - reader.onload = (function(_file,_encoding,_mime) { + reader.onload = (function(_file,_encoding,_mime) { return function(loadEvt) { try { self.insertFile( _file, loadEvt.target.result, _encoding, _mime, pos ); @@ -4271,13 +4276,13 @@ var symbolsMath = [ if (encoding===Storage.Encoding.Base64) reader.readAsDataURL(f); - else + else reader.readAsText(f); } } /*--------------------------------------------------- - Decorations + Decorations -------------------------------------------------- */ UI.prototype.removeDecorations = function(discardSticky,type) { @@ -4289,15 +4294,15 @@ var symbolsMath = [ var newdecs = []; self.decorations.forEach( function(decoration) { if (type && !Util.startsWith(decoration.type,type)) { - // do nothing - newdecs.push(decoration); + // do nothing + newdecs.push(decoration); } else if (discardSticky || !decoration.sticky || (decoration.expire && decoration.expire < now)) { if (decoration.id) { updateDecorationRange(decoration, model); changeAccessor.removeDecoration(decoration.id); - decoration.id = null; + decoration.id = null; } } else { @@ -4306,18 +4311,18 @@ var symbolsMath = [ if (decoration.id && decoration.path === self.editName) { var dec = decoration.options || { isWholeLine : true }; if (decoration.glyphType) dec.glyphMarginClassName = 'glyph-' + decoration.glyphType + '.outdated'; - if (decoration.marginType) dec.linesDecorationsClassName = 'margin-' + decoration.marginType + '.outdated'; + if (decoration.marginType) dec.linesDecorationsClassName = 'margin-' + decoration.marginType + '.outdated'; changeAccessor.changeDecorationOptions(decoration.id, dec ); } else if (decoration.id) { updateDecorationRange(decoration, model); changeAccessor.removeDecoration(decoration.id); - decoration.id = null; + decoration.id = null; } } }); self.decorations = newdecs; - }); + }); } UI.prototype.removeDecorationsOn = function(id,tag) { @@ -4382,7 +4387,7 @@ var symbolsMath = [ if (rng.endColumn != null) { return new Range.Range( rng.startLineNumber, rng.startColumn, rng.endLineNumber, rng.endColumn ); } - else if (rng.lineNumber != null) { // it's a position + else if (rng.lineNumber != null) { // it's a position return new Range.Range( rng.lineNumber, rng.column, rng.lineNumber, rng.column ); } else { @@ -4399,7 +4404,7 @@ var symbolsMath = [ var rulerColorWarning = 'rgba(18,136,18,0.7)'; var rulerColorConcurrent = 'rgba(18,18,255,0.7)'; var rulerColorMerge = 'rgba(18,136,18,0.7)'; - + UI.prototype.showErrors = function( errors, sticky, type ) { var self = this; if (!type) type = "error"; @@ -4407,21 +4412,21 @@ var symbolsMath = [ var decs = []; errors.forEach( function(error) { if (!error.path) error.path = self.docName; - decs.push( { - id: null, + decs.push( { + id: null, type: error.type || type, glyphType: error.glyphType || error.type || type, - sticky: sticky, - outdated: false, - // message: error.message, + sticky: sticky, + outdated: false, + // message: error.message, menu: self.errorMenu, - options: { - htmlMessage: Util.escape(error.message), + options: { + htmlMessage: Util.escape(error.message), isWholeLine: true, overviewRuler: { color: error.type === "warning" ? rulerColorWarning : rulerColorError, position: 4 /* Right */ - }, + }, }, path: error.path, range: newRange(error.range), @@ -4441,13 +4446,13 @@ var symbolsMath = [ var now = Date.now(); merges.forEach( function(merge) { if (!merge.path) merge.path = self.editName; - var dec = { - id: null, + var dec = { + id: null, type: "merge.merge-" + merge.type, - sticky: true, - outdated: false, + sticky: true, + outdated: false, expire: now + (60000), // expire merges after 1 minute? - message: "Merged (" + merge.type + ")" + (merge.content ? ":\n\"" + merge.content + "\"": ""), + message: "Merged (" + merge.type + ")" + (merge.content ? ":\n\"" + merge.content + "\"": ""), path: merge.path, range: newRange( merge.startLine ), options: { @@ -4456,13 +4461,13 @@ var symbolsMath = [ color: rulerColorMerge, position: 4 /* Right */ }, - } + } }; dec.marginType = dec.type; - decs.push(dec); + decs.push(dec); }); self.removeDecorations(false,"merge"); - self.addDecorations(decs); + self.addDecorations(decs); } UI.prototype.showSpellErrors = function( errors ) { @@ -4471,20 +4476,20 @@ var symbolsMath = [ var now = Date.now(); errors.forEach( function(err) { if (!err.path) err.path = self.editName; - var dec = { - id: null, + var dec = { + id: null, tag: err.tag, type: "spellerror", - sticky: true, - outdated: false, + sticky: true, + outdated: false, //glyphType: "spellcheck", //expire: now + (60000), // expire merges after 1 minute? - // message: err.message, + // message: err.message, menu: self.spellCheckMenu, path: err.path, range: newRange(err.range), - options: { - isOverlay:true, + options: { + isOverlay:true, stickiness: 1, /* never grows at edges */ inlineClassName: "spellerror", overviewRuler: { @@ -4493,10 +4498,10 @@ var symbolsMath = [ }, }, }; - decs.push(dec); + decs.push(dec); }); self.removeDecorations(true,"spellerror"); - self.addDecorations(decs); + self.addDecorations(decs); } @@ -4545,8 +4550,8 @@ var symbolsMath = [ // overlapping range? // todo: check column range, perhaps merge messages? if (dec.type == dec2.type && (dec.options==null || dec.options.isWholeLine===true) && - dec.path === dec2.path && - dec.range.startLineNumber <= dec2.range.endLineNumber && + dec.path === dec2.path && + dec.range.startLineNumber <= dec2.range.endLineNumber && dec.range.endLineNumber >= dec2.range.startLineNumber) { if (!dec.outdated && dec2.outdated) { // swap, so we remove the outdated one @@ -4554,7 +4559,7 @@ var symbolsMath = [ dec = dec2; dec2 = self.decorations[j]; } - + // update dec2 to merge if (dec.message !== dec2.message) dec2.message = dec2.message + "\n-----\n" + dec.message; if (dec.marginType && !dec2.marginType) dec2.marginType = dec.marginType; @@ -4582,12 +4587,12 @@ var symbolsMath = [ if (!path) path = self.editName; for (var i = 0; i < self.decorations.length; i++) { var dec = self.decorations[i]; - if (dec.path === path && + if (dec.path === path && dec.range.startLineNumber <= lineNo && dec.range.endLineNumber >= lineNo && (isGlyph ? dec.glyphType : dec.marginType) ) { return dec.message; } - } + } return ""; } @@ -4681,17 +4686,17 @@ var symbolsMath = [ var menu = dec.menu; if (menu) { var text = self.editor.getModel().getValueInRange(r); - setTimeout( function() { - menu.startShowingAt(null, r, text, dec); - }, 50 ); + setTimeout( function() { + menu.startShowingAt(null, r, text, dec); + }, 50 ); } }); } /* -------------------------------------------------------------- - View synchronization + View synchronization -------------------------------------------------------------- */ - + UI.prototype.dispatchViewEvent = function( ev ) { var self = this; // we use "*" since sandboxed iframes have a null origin @@ -4717,7 +4722,7 @@ var symbolsMath = [ } } - UI.prototype.syncView = function( options, startLine, endLine, cursorLine ) + UI.prototype.syncView = function( options, startLine, endLine, cursorLine ) { var self = this; try { @@ -4729,7 +4734,7 @@ var symbolsMath = [ cursorLine = self.editor.getPosition().lineNumber; } if (startLine==null) { - var editView = self.editor.getView(); + var editView = self.editor.getView(); var lines = editView.viewLines; var rng = lines._currentVisibleRange; startLine = rng.startLineNumber; @@ -4752,12 +4757,12 @@ var symbolsMath = [ if (lineNo === self.lastLineNo && !options.force) return false; self.lastLineNo = lineNo; - // use physical textline; + // use physical textline; // start-, end-, cursor-, and lineNo are all view lines. // if wrapping is enabled, this will not correspond to the actual text line var textLine = self.viewToTextLine(lineNo); var slines = null; - + // find the element in the view tree var event = options; event.eventType = "scrollToLine"; @@ -4768,9 +4773,9 @@ var symbolsMath = [ event.lineCount = lineCount; event.sourceName = self.editName === self.docName ? null : self.editName; event.height = self.view.clientHeight; - + // for separate viewer - // localStorage.setItem("viewer-scroll",JSON.stringify(event)); + // localStorage.setItem("viewer-scroll",JSON.stringify(event)); // post scroll message to view iframe self.dispatchViewEvent(event); @@ -4792,7 +4797,7 @@ var symbolsMath = [ self.onFileDelete(ev.file); } else if (ev.type === "flush") { - self.flush( ev.path ); + self.flush( ev.path ); } else if (ev.type === "destroy") { self.dispatchViewEvent( { @@ -4802,7 +4807,7 @@ var symbolsMath = [ } UI.prototype.onFileDelete = function(file) { - var self = this; + var self = this; if (Util.isReferMime(file.mime)) { self.dispatchViewEvent( { eventType: "file-delete", @@ -4833,7 +4838,7 @@ var symbolsMath = [ var folder = fullFolder; if (folder.length > 30) folder = "..." + folder.substr(folder.length-30); var prefix = "<span class='folder'>" + folder + (folder ? "/" : "") + "</span>"; - + var postfix = self.displayFile(file); var fileDisplay = prefix + postfix; if (!self.fileDisplay || self.fileDisplay !== fileDisplay) { // prevent too many calls to setInnerHTML @@ -4841,7 +4846,7 @@ var symbolsMath = [ var title = "//" + self.storage.remote.displayName + fullFolder + (fullFolder ? "/" : "") + file.path; self.editSelectHeader.innerHTML = "<span title='" + Util.escape(title) + "'>" + fileDisplay + "</span>"; } - if (self.editContent !== file.content) { // only update edit text if content update + if (self.editContent !== file.content) { // only update edit text if content update self.setEditText(file.content); } } @@ -4856,18 +4861,18 @@ var symbolsMath = [ } UI.prototype.saveTo = function() { - var self = this; + var self = this; return Storage.saveAs(self.storage,self.docName).then( function(res) { - if (!res || !res.storage) throw new Error("cancel"); + if (!res || !res.storage) throw new Error("cancel"); return self.withSyncSpinner( function() { - return res.storage.syncOrCommit().then( function() { // ensure we can save the new storage object + return res.storage.syncOrCommit().then( function() { // ensure we can save the new storage object return self.setStorage(res.storage,res.docName).then( function() { // .. before setting this as our new storage return res.docName; - }); + }); }); }); - }); - } + }); + } UI.prototype.withSyncSpinner = function( makePromise) { var self = this; @@ -4881,8 +4886,8 @@ var symbolsMath = [ var self = this; return self.event( "", "", State.Syncing, function() { if (!self.isConnected) { - return self.login().then( function() { - return self._synchronize(pullOnly); + return self.login().then( function() { + return self._synchronize(pullOnly); }); } else if (self.storage && self.storage.remote.readonly) { @@ -4897,7 +4902,7 @@ var symbolsMath = [ UI.prototype.pull = function() { var self = this; return self.event( "", "", State.Syncing, function() { - return self.login().then( function() { + return self.login().then( function() { return self._synchronize(true); }); }); @@ -4907,7 +4912,7 @@ var symbolsMath = [ var self = this; self.lastSync = Date.now(); if (self.storage) { - var cursors = {}; + var cursors = {}; var line0 = self.editor.getPosition().lineNumber; cursors["/" + self.docName] = line0; self.showConcurrentUsers(false); @@ -4933,10 +4938,10 @@ var symbolsMath = [ UI.prototype.spellCheck = function() { var self = this; return self.anonEvent( function() { - var ctx = { - round: 0, - path: self.editName, - show: function(errors) { return self.showSpellErrors(errors); } + var ctx = { + round: 0, + path: self.editName, + show: function(errors) { return self.showSpellErrors(errors); } }; return self.spellChecker.check( self.editor.getValue(), ctx ).then( function(res) { Util.message("Spell check done.", Util.Msg.Status); @@ -4950,17 +4955,17 @@ var symbolsMath = [ return "<li>" + upd.version + (upd.date ? ", " + upd.date : "") + "<ul>" + upd.updates.map( function(item) { return "<li>" + Util.miniMarkdown(item) + "</li>"; - }).join("") + - "</ul></li>"; + }).join("") + + "</ul></li>"; } - self.anonEvent( function() { + self.anonEvent( function() { var shortDigest = "(" + self.version.digest.substr(0,6) + ")"; var shortDate = self.version.date.substr(0,10); var log = (self.version.log instanceof Array ? self.version.log : [self.version.log]); var logdiv = "<ul class='version-updates'>" + log.map( showUpdate ).join("\n") + "</ul>" - var htmlMessage = "<div class='version-display'>" + + var htmlMessage = "<div class='version-display'>" + (!self.version.log[0].alert ? "" : "<div class='version-alert'>" + Util.miniMarkdown(self.version.log[0].alert) + "</div>") + (!self.version.log[0].message ? "" : "<div class='version-message'>" + Util.miniMarkdown(self.version.log[0].message) + "</div>") + (logdiv) + @@ -4970,10 +4975,10 @@ var symbolsMath = [ } - // object + // object return UI; })(); // module return UI; -}); \ No newline at end of file +}); From 3a8b3b5ccdd5d59f6c56c4017030a6a865205b26 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Sun, 9 Feb 2020 09:16:40 -0800 Subject: [PATCH 36/46] update build instructions --- Jakefile.js | 76 ++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 16786026..550d17e0 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -34,7 +34,7 @@ var webclient = path.join(web,"client"); // Then set it to the older version: // // > cd koka-0.6 -// > git checkout v0.6.0-dev +// > git checkout v0.6.1-dev (or v0.6.x-dev) // > npm install // // and build the release version: @@ -47,11 +47,11 @@ var kokaExe = path.join(kokaDir,"out/release/koka-0.6.0-dev") var testDir = "test"; var kokaFlags = "-i" + sourceDir + " -i" + libraryDir + " " + (process.env.kokaFlags || ""); -var kokaCmd = kokaExe + " " + kokaFlags + " -c -o" + outputDir + " --outname=" + main + " " +var kokaCmd = kokaExe + " " + kokaFlags + " -c -o" + outputDir + " --outname=" + main + " " //----------------------------------------------------- -// Tasks: compilation +// Tasks: compilation //----------------------------------------------------- task("default",["madoko"]); @@ -68,7 +68,7 @@ task("madoko", [], function(cs) { fixVersion(); var cmd = kokaCmd + " -v " + args + " " + maincli; jake.logger.log("> " + cmd); - jake.exec(cmd, {interactive: true}, function() { + jake.exec(cmd, {interactive: true}, function() { jake.cpR(path.join(sourceDir,"cli.js"), outputDir); ["monarch/monarch.js", "csl/bibtex-parse.js", @@ -79,7 +79,7 @@ task("madoko", [], function(cs) { ].forEach( function(contrib) { jake.cpR(path.join(contribDir,contrib), outputDir); }); - complete(); + complete(); }) },{async:true}); @@ -116,7 +116,7 @@ task("copystyles", [], function() { }); //----------------------------------------------------- -// Tasks: clean +// Tasks: clean //----------------------------------------------------- desc("remove all generated files."); task("clean", function() { @@ -129,7 +129,7 @@ task("clean", function() { }); //----------------------------------------------------- -// Tasks: web +// Tasks: web //----------------------------------------------------- desc("build web madoko") task("web", [], function() { @@ -148,7 +148,7 @@ task("justcopy", [], function() { // copy all madoko sources var js = new jake.FileList().include(path.join(outputDir,"*.js")); copyFiles(outputDir,js.toArray(),path.join(webclient,"lib")); - + // copy style, language, and image files jake.mkdirP(path.join(webclient,path.join(styleDir,"lang"))); jake.mkdirP(path.join(webclient,path.join(styleDir,"images"))); @@ -162,17 +162,17 @@ task("justcopy", [], function() { .include(path.join(styleDir,"locales","*.xml")) .include(path.join(styleDir,"scripts","*.js")); copyFiles(styleDir,js.toArray(),path.join(webclient,styleDir)); - + js = new jake.FileList().include(path.join(contribDir,"styles","*.css")) .include(path.join(contribDir,"styles","*.mdk")) .include(path.join(contribDir,"styles","*.bib")) .include(path.join(contribDir,"styles","*.cls")); copyFiles(path.join(contribDir,"styles"),js.toArray(),path.join(webclient,styleDir)); - + js = new jake.FileList().include(path.join(contribDir,"images","*.png")) .include(path.join(contribDir,"images","*.pdf")); copyFiles(contribDir,js.toArray(),path.join(webclient,styleDir)); - + jake.mkdirP(path.join(webclient,"templates","style")); var templateDir = path.join(contribDir,"templates"); js = new jake.FileList().include(path.join(templateDir,"*")) @@ -195,13 +195,13 @@ task("justcopy", [], function() { var sty = new jake.FileList().include(path.join(styleDir,"*.sty")); // copyFiles(styleDir,sty.toArray(),localTexDir); copyFiles(styleDir,sty.toArray(),path.join(webclient,styleDir)) -}); +}); task("webcopy",["web","justcopy"], function() { }); //----------------------------------------------------- -// Tasks: test +// Tasks: test //----------------------------------------------------- desc("run tests.\n test[--extra] # run tests for extensions."); task("test", ["madoko"], function() { @@ -210,29 +210,29 @@ task("test", ["madoko"], function() { testCmd = "node test " + testFlags + args.filter(function(s){ return (s.substr(0,2) == "--"); }).join(" ") jake.log("> " + testCmd) jake.exec(testCmd, {printStdout: true, printStderr: true}) -}); +}); //----------------------------------------------------- -// Tasks: bench +// Tasks: bench //----------------------------------------------------- desc("run benchmark.\n bench[--quick] # run the bench mark in quick mode."); task("bench", [], function() { - testFlags=(process.env.testFlags||"") + testFlags=(process.env.testFlags||"") args = Array.prototype.slice.call(arguments) testCmd = "node test --bench --gfm " + testFlags + args.join(" ") jake.log("> " + testCmd) jake.exec(testCmd,{interactive:true}) -}); +}); //----------------------------------------------------- // Tasks: doc //----------------------------------------------------- -desc("generate documentation.\n doc[--pdf] # generate pdf too (using LaTeX).") +desc("generate documentation.\n doc[--pdf] # generate pdf too (using LaTeX).") task("doc", [], function() { var docout = "out"; args = Array.prototype.slice.call(arguments).join(" "); var pngs = new jake.FileList().include(path.join("doc","*.png")); - copyFiles("doc",pngs.toArray(),path.join("doc",docout)); + copyFiles("doc",pngs.toArray(),path.join("doc",docout)); process.chdir("doc"); mdCmd = "node ../lib/cli.js -v --odir=" + docout + " " + args + " reference.mdk mathdemo.mdk slidedemo.mdk"; jake.log("> " + mdCmd); @@ -268,19 +268,19 @@ task("publish", [], function () { //----------------------------------------------------- // Tasks: line count //----------------------------------------------------- -desc("line count.") +desc("line count.") task("linecount", [], function() { var sources = new jake.FileList().include(path.join(sourceDir,"*.kk")); // var sources = new jake.FileList().include(path.join("lib","*.js")); var src = sources.toArray().map( function(file) { return fs.readFileSync(file,{encoding:"utf8"}); }).join() //xsrc = src.replace(/^[ \t]*\/\*[\s\S]*?\*\/[ \t\r]*\n|^[ \t]*\/\/.*\n/gm, "") comments = lineCount(src.match(/^[ \t]*\/\*[\s\S]*?\*\/[ \t\r]*\n|^[ \t]*\/\/.*\n/gm).join()) - blanks = src.match(/\r?\n[ \t]*(?=\r?\n)/g).length + blanks = src.match(/\r?\n[ \t]*(?=\r?\n)/g).length total = lineCount(src) - jake.log("total lines : " + total) + jake.log("total lines : " + total) jake.log(" source lines : " + (total-comments-blanks)) - jake.log(" comment lines: " + comments) - jake.log(" blank lines : " + blanks ) + jake.log(" comment lines: " + comments) + jake.log(" blank lines : " + blanks ) }); function lineCount(s) { @@ -291,8 +291,8 @@ function lineCount(s) { // Tasks: documentation generation & editor support //----------------------------------------------------- var cmdMarkdown = "node " + path.join(outputDir,maincli + ".js"); - -desc("create source documentation.") + +desc("create source documentation.") task("sourcedoc", [], function(mode) { jake.logger.log("build documentation"); var out = outputDir + "doc" @@ -322,11 +322,11 @@ task("sublime", function(sversion) { var sversion = sversion || "2" if (process.env.APPDATA) { sublime = path.join(process.env.APPDATA,"Sublime Text " + sversion); - } + } else if (process.env.HOME) { - if (path.platform === "darwin") + if (path.platform === "darwin") sublime = path.join(process.env.HOME,"Library","Application Support","Sublime Text " + sversion); - else + else sublime = path.join(process.env.HOME,".config","sublime-text-" + sversion); } sublime = path.join(sublime,"Packages"); @@ -339,7 +339,7 @@ task("sublime", function(sversion) { var sublimeCS = path.join(sublime,dirCS); jake.mkdirP(sublimeCS); - jake.cpR(path.join("support","sublime-text","Snow.tmTheme"),sublimeCS); + jake.cpR(path.join("support","sublime-text","Snow.tmTheme"),sublimeCS); jake.cpR(path.join("support","sublime-text","madoko"),sublime); } }); @@ -356,7 +356,7 @@ var usageInfo = [ function showHelp() { jake.logger.log(usageInfo); jake.showAllTaskDescriptions(jake.program.opts.tasks); - process.exit(); + process.exit(); } desc("show this information"); @@ -385,12 +385,12 @@ function getVersion() { } } return "<unknown>" -} +} function fixVersion(fname) { fname = fname || path.join(sourceDir,"version.kk"); - + var version = getVersion(); var content1 = fs.readFileSync(fname,{encoding: "utf8"}); if (content1) { @@ -399,14 +399,14 @@ function fixVersion(fname) { if (content1 !== content2) { jake.logger.log("updating version string in '" + fname + "' to '" + version + "'") fs.writeFileSync(fname,content2,{encoding: "utf8"}); - } + } } } function fileExist(fileName) { var stats = null; try { - stats = fs.statSync(fileName); + stats = fs.statSync(fileName); } catch(e) {}; return (stats != null); @@ -417,12 +417,12 @@ function fileExist(fileName) { function copyFiles(rootdir,files,destdir) { rootdir = rootdir || ""; rootdir = rootdir.replace(/\\/g, "/"); - jake.mkdirP(destdir); + jake.mkdirP(destdir); files.forEach(function(filename) { // make relative var destname = path.join(destdir,(rootdir && filename.lastIndexOf(rootdir,0)===0 ? filename.substr(rootdir.length) : filename)); - var logfilename = (filename.length > 30 ? "..." + filename.substr(filename.length-30) : filename); - var logdestname = (destname.length > 30 ? "..." + destname.substr(destname.length-30) : destname); + var logfilename = (filename.length > 30 ? "..." + filename.substr(filename.length-30) : filename); + var logdestname = (destname.length > 30 ? "..." + destname.substr(destname.length-30) : destname); //jake.logger.log("cp -r " + logfilename + " " + logdestname); jake.cpR(filename,path.dirname(destname)); }) From 6680cfe0d966414b4be2b06e743a2c7a34659a67 Mon Sep 17 00:00:00 2001 From: Daan Leijen <daan@microsoft.com> Date: Fri, 14 Feb 2020 16:37:11 -0800 Subject: [PATCH 37/46] update madoko-local to have more liberal timeouts --- support/madoko-local/package.json | 64 +++++++++++++++--------------- support/madoko-local/src/config.js | 6 +-- support/madoko-local/src/init.js | 6 +-- support/madoko-local/src/util.js | 4 +- 4 files changed, 41 insertions(+), 39 deletions(-) diff --git a/support/madoko-local/package.json b/support/madoko-local/package.json index 74c67895..54399b7a 100644 --- a/support/madoko-local/package.json +++ b/support/madoko-local/package.json @@ -1,22 +1,24 @@ { - "name" : "madoko-local", - "author" : "Daan Leijen, Microsoft Research", - "version" : "0.9.1", - "homepage" : "http://madoko.codeplex.com", - "description" : "Madoko-Local provides local disk access to Madoko.net", - "licenses": [{ - "type" : "Apache License 2.0", - "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" - }], + "name": "madoko-local", + "author": "Daan Leijen, Microsoft Research", + "version": "0.9.2", + "homepage": "http://madoko.codeplex.com", + "description": "Madoko-Local provides local disk access to Madoko.net", + "licenses": [ + { + "type": "Apache License 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], "keywords": [ - "Madoko", - "Markdown", - "LaTeX", - "Koka", - "Microsoft", - "Scholarly", - "Academic", - "Local" + "Madoko", + "Markdown", + "LaTeX", + "Koka", + "Microsoft", + "Scholarly", + "Academic", + "Local" ], "preferGlobal": true, "main": "./src/cli.js", @@ -32,27 +34,27 @@ "readme.md", "license.txt" ], - "engines" : { + "engines": { "node": ">=0.10.0" }, "dependencies": { - "amdefine" : ">=0.0.4", - "requirejs" : ">=2.1", - "mkdirp" : ">=1.0.0", - "rimraf" : ">=2.4.4", - "mime" : ">=1.3.4", - "commander" : ">=2.8.1", - "express" : ">=4.1.1", - "body-parser" : ">=1.0.2", - "cookie-session" : "=1.0.2", - "os-homedir" : ">=1.0.0", - "open" : ">=0.0.5" + "amdefine": ">=0.0.4", + "requirejs": ">=2.1", + "mkdirp": ">=1.0.0", + "rimraf": ">=2.4.4", + "mime": ">=1.3.4", + "commander": ">=2.8.1", + "express": ">=4.1.1", + "body-parser": ">=1.0.2", + "cookie-session": "=1.0.2", + "os-homedir": ">=1.0.0", + "open": ">=0.0.5" }, "devDependencies": { - "jake" : ">=0.7.6" + "jake": ">=0.7.6" }, "repository": { "type": "hg", - "url" : "https://hg.codeplex.com/madoko" + "url": "https://hg.codeplex.com/madoko" } } diff --git a/support/madoko-local/src/config.js b/support/madoko-local/src/config.js index 01d4d390..303c9c70 100644 --- a/support/madoko-local/src/config.js +++ b/support/madoko-local/src/config.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -9,7 +9,7 @@ if (typeof define !== 'function') { var define = require('amdefine')(module) } define([],function() { -var version = "0.9"; +var version = "0.9.2"; var main = "madoko-local"; return { @@ -17,4 +17,4 @@ return { main : main, }; -}); \ No newline at end of file +}); diff --git a/support/madoko-local/src/init.js b/support/madoko-local/src/init.js index 46be753b..cc520b3b 100644 --- a/support/madoko-local/src/init.js +++ b/support/madoko-local/src/init.js @@ -49,14 +49,14 @@ var config = { verbose : 0, limits : { fileSize : 64*mb, - logFlush : 1*minute, + logFlush : 5*minute, timeoutPDF : 2*minute, - timeoutMath : 1*minute, + timeoutMath : 2*minute, }, run : null, // program to run Madoko locally rundir : null, // directory under which to run LaTeX. (<configdir>/run) runflags : "", // extra run flags. - rmdirDelay: 5*second, // after this amount the run directory gets removed + rmdirDelay: 10*second, // after this amount the run directory gets removed mime : null, // gets set to Express.static.mime launch : false, // if true, launch the browser at startup } diff --git a/support/madoko-local/src/util.js b/support/madoko-local/src/util.js index 32a70ef6..63f01151 100644 --- a/support/madoko-local/src/util.js +++ b/support/madoko-local/src/util.js @@ -151,12 +151,12 @@ function ensureDir(dir) { // remove everything in dir recursively function removeDirAll(dir) { - return new Promise( function(cont) { rmdirRF(dir,cont); } ); + return new Promise( function(cont) { return rmdirRF(dir,{maxRetries:5},cont); } } // remove a directory if it is empty function removeDir(dir) { - return new Promise( function(cont) { Fs.rmdir(dir,cont); } ); + return new Promise( function(cont) { return Fs.rmdir(dir,{maxRetries:5},cont); }); } function writeFile( fpath, content, options ) { From c13412a16c5f3e09e0f9d8eca92b511cb2f1cd7d Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Fri, 14 Feb 2020 23:22:20 -0800 Subject: [PATCH 38/46] enable running dvisvgm in parallel --- src/mathStaticRun.kk | 186 ++++++++++++++++++++++++------------------ src/options.kk | 80 +++++++++--------- src/optionsSandbox.kk | 11 +-- src/process-inline.js | 32 +++++++- src/process.kk | 7 +- 5 files changed, 187 insertions(+), 129 deletions(-) diff --git a/src/mathStaticRun.kk b/src/mathStaticRun.kk index 0001dd8a..78bd90cf 100644 --- a/src/mathStaticRun.kk +++ b/src/mathStaticRun.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013-2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -64,7 +64,7 @@ public function runMathStatic( content : string, inName: string, outName : strin } } } - } + } } function checkDviSvgVersion( opts : options, texNamePlain, texNameFull, cont : (options) -> io () ) : io () { @@ -109,13 +109,13 @@ function fileRenderPng( fname : string ) : io () { function mathSnippetsRender( mode: mathkind, mrender : mathrender, texcmd: string, - srcName: string, texName : string, xopts : options, - content : string, oldMath : string, + srcName: string, texName : string, xopts : options, + content : string, oldMath : string, pages : pages, - continue : (string) -> io () ) : io () + continue : (string) -> io () ) : io () { function rendermsg(ext) { - xopts.print( "running " + (texcmd.stemname + " on math...").fill(19) + "(" + + xopts.print( "running " + (texcmd.stemname + " on math...").fill(19) + "(" + mode.show.fill(5) + " -> " + ext.substr(1) + " -> " + mrender.show + ")" ) } if (mrender.isSvg) { @@ -124,17 +124,17 @@ function mathSnippetsRender( mode: mathkind, mrender : mathrender, texcmd: strin if (pages.isNil) return continue(dviext) rendermsg(dviext) runLaTeX( srcName, texName, texcmd, texargs, xopts, content, "", 0, False, fun(err) { - continue(if (err != 0) then "" else dviext) + continue(if (err != 0) then "" else dviext) }) } // run for png; elif (!(mode.isFull && texcmd.isPlainLatex)) { - // if we are not running in pdf with empty pdf latex + // if we are not running in pdf with empty pdf latex val pdfext = if (texcmd.isPlainLatex) then ".dvi" else ".pdf" if (pages.isNil) return continue(pdfext) rendermsg(pdfext) runLaTeX( srcName, texName, texcmd, "", xopts, content, "", 0, False, fun(err) { - continue(if (err!=0) then "" else pdfext) + continue(if (err!=0) then "" else pdfext) }) } else { @@ -155,7 +155,7 @@ function mathSnippetsRender( mode: mathkind, mrender : mathrender, texcmd: strin system(dvipsCmd,fun(err,stdout,stderr) { if (err != 0) { xopts.printErr("> " + dvipsCmd) - xopts.printErr("error: failure while typesetting math: \n" + stdout + stderr) + xopts.printErr("error: failure while typesetting math: \n" + stdout + stderr) continue("") } else { @@ -163,7 +163,7 @@ function mathSnippetsRender( mode: mathkind, mrender : mathrender, texcmd: strin system(ps2pdfCmd, fun(err2,stdout2,stderr2) { if (err2 != 0) { xopts.printErr("> " + ps2pdfCmd) - xopts.printErr("error: failure while typesetting math: \n" + stdout2 + stderr2) + xopts.printErr("error: failure while typesetting math: \n" + stdout2 + stderr2) continue("") } else { @@ -173,11 +173,11 @@ function mathSnippetsRender( mode: mathkind, mrender : mathrender, texcmd: strin } },xopts.processTimeout,dir) }) - } + } } function texToDvi( texcmd : string ) : (string,string) { - if (texcmd.isXelatex) then (" --no-pdf", ".xdv") + if (texcmd.isXelatex) then (" --no-pdf", ".xdv") elif (texcmd.isFulllatex) then (" --output-format=dvi", ".dvi") elif (texcmd.isLualatex) then (" --output-format=dvi", ".dvi") else ("", ".dvi") @@ -205,21 +205,46 @@ function pagesShow( pages : pages ) : string { val (lo,hi) = rng lo.show + (if (lo>=hi) then "" else "-" + hi.show) }).join(",") -} +} function dvipngPages( pages : pages ) : string { pages.map( fun(rng) { val (lo,hi) = rng "-pp " + lo.show + (if (lo>=hi) then "" else "-" + hi.show) }).join(" ") -} +} -function dvisvgPages( pages : pages ) : string { - "-p" + pages.map( fun(rng) { - val (lo,hi) = rng - lo.show + (if (lo>=hi) then "" else "-" + hi.show) - }).join(",") -} +function groupPages( gcount : int, count : int, cur : pages, acc : list<pages>, pages : pages ) : div list<pages> { + match(pages) { + Nil -> Cons(cur.reverse,acc).reverse + Cons((lo,hi),rs) -> { + val n = hi - lo + 1; + val m = gcount - count; + if (m >= n) + then groupPages( gcount, count + n, Cons((lo,hi),cur), acc, rs) + elif (m <= 0) + then groupPages( gcount, 0, [], Cons(cur.reverse,acc), pages) + else { + val mid = lo + m - 1 + val r1 = (lo,mid) + val r2 = (mid + 1, hi) + groupPages( gcount, count + m, Cons(r1,cur), acc, Cons(r2,rs) ) + } + } + } +} + +function dvisvgPages( pages : pages, concurrency : int ) : list<string> { + val n = pages.count + val g = if (n < 10 || concurrency <= 0) then 0 else ((n / concurrency) + 1); + val pss = if (g == 0) then [pages] else unsafeNoDiv{ groupPages(g,0,[],[],pages) } + pss.map( fun(ps) { + "-p" + ps.map( fun(rng) { + val (lo,hi) = rng + lo.show + (if (lo>=hi) then "" else "-" + hi.show) + }).join(",") + }) +} function convertPages( pages : pages ) : string { "[" + pages.map( fun(rng) { @@ -236,13 +261,13 @@ function convertSuffix( pages : pages ) : string { } -function mathToImg( namePlain : string, nameFull : string, xopts : options, - plainPages : pages, fullPages : pages, - continue : (int,double,int,double) -> io () ) : io () -{ - val (plainconvCmd,plainPageAdj,plainBboxAdj) +function mathToImg( namePlain : string, nameFull : string, xopts : options, + plainPages : pages, fullPages : pages, + continue : (int,double,int,double) -> io () ) : io () +{ + val (plainconvCmd,plainPageAdj,plainBboxAdj) = mathConvImgCmd(Plain, namePlain, xopts.math.getMathRender, xopts.math, plainPages ) - val (fullconvCmd,fullPageAdj,fullBboxAdj) + val (fullconvCmd,fullPageAdj,fullBboxAdj) = mathConvImgCmd(Full, nameFull, xopts.math.getMathRenderFull, xopts.math, fullPages ) // make sure the output directory exists @@ -250,7 +275,7 @@ function mathToImg( namePlain : string, nameFull : string, xopts : options, if (!(fexistsSync(outDir))) { xopts.print("create image directory: " + outDir) mkdirp(outDir) - } + } mathCmdToImg( plainconvCmd, namePlain.dirname, Plain, plainPages, xopts, fun(err1) { mathCmdToImg( fullconvCmd, nameFull.dirname, Full, fullPages, xopts, fun(err2) { @@ -269,23 +294,25 @@ function mathConvImgCmd( mode : mathkind, fname : string, mrender : mathrender, // extension should be dvi or xdv // add 0.2pt (~0.07mm of extra bounding box for better rendering of svg's) val fuzz = 0.2 - val cmd = opts.dvisvg.quote + - (if (opts.svgFontFormat=="" || opts.svgFontFormat=="none") - then " -n" - elif (opts.svgFontFormat=="svg") - then "" - else " --font-format=" + opts.svgFontFormat.quote) + - (if (fuzz > 0.0) then " -b" + fuzz.showFixed(1) + "pt" else "") + - " -e -j -v3 -d" + opts.svgPrec.show + " " + - dvisvgPages(pages) + - " -o " + (baseImg + "-%1p.svg").quote + - " " + fname.basename.quote + val cmd = dvisvgPages(pages,opts.concurrency).map( fun(pageRange) { + opts.dvisvg.quote + + (if (opts.svgFontFormat=="" || opts.svgFontFormat=="none") + then " -n" + elif (opts.svgFontFormat=="svg") + then "" + else " --font-format=" + opts.svgFontFormat.quote) + + (if (fuzz > 0.0) then " -b" + fuzz.showFixed(1) + "pt" else "") + + " -e -j -v3 -d" + opts.svgPrec.show + " " + + pageRange + + " -o " + (baseImg + "-%1p.svg").quote + + " " + fname.basename.quote + }).join(";") // run in parallel (cmd,0,fuzz) } Png | fname.extname==".dvi" -> { - val cmd = opts.dvipng.quote + " -T tight -z9 -bg Transparent" + + val cmd = opts.dvipng.quote + " -T tight -z9 -bg Transparent" + " -D" + opts.dpi.show + - " " + dvipngPages(pages) + + " " + dvipngPages(pages) + " -o " + (baseImg + "-%d.png").quote + " " + fname.stemname.quote (cmd,0,0.0) @@ -293,25 +320,25 @@ function mathConvImgCmd( mode : mathkind, fname : string, mrender : mathrender, Png -> { // extension should be pdf val cmd = opts.convert.quote + " -trim -density " + opts.dpi.show + " " + (fname.basename + pages.convertPages).quote + " " + - (baseImg + pages.convertSuffix + ".png").quote - (cmd,~1,0.0) + (baseImg + pages.convertSuffix + ".png").quote + (cmd,~1,0.0) } } } -function mathCmdToImg( cmd : string, dir : string, mode : mathkind, pages : pages, xopts : options, - continue : (int) -> io () ) : io () -{ +function mathCmdToImg( cmd : string, dir : string, mode : mathkind, pages : pages, xopts : options, + continue : (int) -> io () ) : io () +{ if (pages.isNil) return continue(0) - xopts.print("generating math images... (" + mode.show.fill(5) + ") (" + pages.pagesShow + ")") + xopts.print("generating math images... (" + mode.show.fill(5) + ") (" + pages.pagesShow + ") (concurrency " + xopts.math.concurrency.show + ")") xopts.print("> " + cmd,3) system(cmd,fun(err2,stdout2,stderr2) { //trace("done system cmd") - val output = stdout2 + stderr2 + val output = stdout2 + stderr2 if (err2 != 0) { xopts.printErr("> " + cmd) - xopts.printErr("error: failure while typesetting math: \n" + output) + xopts.printErr("error: failure while typesetting math: \n" + output) if (output.contains("Invalid Parameter -")) then { xopts.printErr("hint: perhaps you forgot to install ImageMagick?\n (http://www.imagemagick.org/script/binary-releases.php)") } @@ -329,7 +356,7 @@ val rxPSWarning = regex(@"^(warning: .*\r\n)+$",ignoreCase=True) function mathDimAnalyse( dimxName : string, xopts : options, plainPages : pages, fullPages : pages, texNamePlain : string, texNameFull : string, plainPageAdj : int, plainBboxAdj : double, - fullPageAdj : int, fullBboxAdj : double, + fullPageAdj : int, fullBboxAdj : double, continue : (maybe<(dict<mathinfo>,string)>) -> io () ) : io () { xopts.print("analyse and embed math images.") val dims2 = extendDim(plainPages, fullPages, texNamePlain, texNameFull, plainPageAdj, plainBboxAdj, fullPageAdj, fullBboxAdj, dimxName, xopts ) @@ -353,39 +380,39 @@ function mathImageGC( newDim : dict<mathinfo>, oldDim : dict<mathinfo>, outDir : } } -function extendDim( plainPages : pages, fullPages : pages, +function extendDim( plainPages : pages, fullPages : pages, texNamePlain : string, texNameFull : string, plainPageAdj : int, plainBboxAdj : double, - fullPageAdj : int, fullBboxAdj : double, + fullPageAdj : int, fullBboxAdj : double, dimxFile : string, opts : options ) : io string { - var imageSize := 0 + var imageSize := 0 var imageCount := 0 var embedSize := 0 - var embedCount := 0 + var embedCount := 0 val dim = opts.math.dim - - function dimLine( render:mathrender, mode : mathkind, fname : string, - pages : pages, pageAdj : int, bboxAdj : double, line : string ) + + function dimLine( render:mathrender, mode : mathkind, fname : string, + pages : pages, pageAdj : int, bboxAdj : double, line : string ) { match(line.find(rxDimLine)) { Nothing -> line - Just(cap) -> { + Just(cap) -> { val pageNo = cap.groups[2].parseInt.maybe(0,id) val digest = cap.groups[3] val imageStem = fname.stemname + "-" + (pageNo + pageAdj).show val imageName = combine(opts.math.imgDir, imageStem.changeExt(render.show) ) val imageFile = combine(dimxFile.dirname,imageName) //trace("read image: " + pageNo.show + ": " + imageFile + ": " + imageFile) - - val (iwidth,iheight,size,embed) + + val (iwidth,iheight,size,embed) = match(dim[digest]) { - Just(mi) | !(pageNo.inside(pages)) - -> { + Just(mi) | !(pageNo.inside(pages)) + -> { //trace("known image: " + pageNo.show + ": " + imageFile + ": " + digest) (mi.iwidth,mi.iheight,mi.size,mi.originalData) } - _ -> { + _ -> { val res = match(render) { Png -> { @@ -396,9 +423,9 @@ function extendDim( plainPages : pages, fullPages : pages, } } val imageDigestFile = combine(imageFile.dirname,"math-" + digest + imageFile.extname) - tryUnlinkSync(imageDigestFile) + tryUnlinkSync(imageDigestFile) if (res.field4=="" && res.thd>0) then { - // give robust name + // give robust name rename(imageFile,imageDigestFile) } else { @@ -407,28 +434,28 @@ function extendDim( plainPages : pages, fullPages : pages, res } } - + if (embed == "") then { imageSize := imageSize + size - imageCount := imageCount + 1 + imageCount := imageCount + 1 } else { embedSize := embedSize + size embedCount := embedCount + 1 - } + } cap.groups[1] + "," + iwidth.show + "pt," + iheight.show + "pt," + bboxAdj.show + "pt," + - size.show + "," + render.showMime + (if (embed=="") then "" else (", " + embed)) + size.show + "," + render.showMime + (if (embed=="") then "" else (", " + embed)) } } } val plainDims = readTextFileDef(texNamePlain.changeExt(".dim"),"") val fullDims = readTextFileDef(texNameFull.changeExt(".dim"),"") - val dimsx = plainDims.lines.list.map( fun(line) { + val dimsx = plainDims.lines.list.map( fun(line) { dimLine(opts.math.getMathRender,Plain,texNamePlain,plainPages,plainPageAdj,plainBboxAdj,line) }) + - fullDims.lines.list.map( fun(line) { + fullDims.lines.list.map( fun(line) { dimLine(opts.math.getMathRenderFull,Full,texNameFull,fullPages,fullPageAdj,fullBboxAdj, line) }) - + val txt = dimsx.join("\n") tryWriteTextFile( dimxFile, txt ) if (opts.verbose >= 2) { @@ -451,7 +478,7 @@ function analyzeImagePng( imageFile, opts : options ) : io (double, double, int, val base64 = buf.toBase64() val pxwidth = buf.readInt4(16,True) val pxheight= buf.readInt4(20,True) - + val ppt = (if (opts.math.dpi > 0) then opts.math.dpi.double else 72.27) / 72.27 val iheight = pxheight.double / ppt val iwidth = pxwidth.double / ppt @@ -461,8 +488,8 @@ function analyzeImagePng( imageFile, opts : options ) : io (double, double, int, val embed = if (base64!="" && base64.length + pngprefix.length < opts.math.embedLimit) then pngprefix + base64 else "" - //trace("embed: " + embed.length.show + ", " + opts.math.embedLimit.show) - val size = if (embed.isEmpty) then buf.length else embed.length + //trace("embed: " + embed.length.show + ", " + opts.math.embedLimit.show) + val size = if (embed.isEmpty) then buf.length else embed.length (iwidth,iheight,size,embed) } } @@ -478,11 +505,11 @@ function analyzeImageSvg( imageFile, opts : options, mode : mathkind, digest : s val (iheight,svg2) = svg1.svgExtractDim(rxSvgHeight) val (iwidth,svg3) = svg2.svgExtractDim(rxSvgWidth) val svg = svg3.svgCompress() - val encoded = "data:image/svg+xml;charset=utf8," + svg.encodeGlyphIds(mode).scopeStyles(digest) + val encoded = "data:image/svg+xml;charset=utf8," + svg.encodeGlyphIds(mode).scopeStyles(digest) //trace("svg: " + imageFile + ", height: " + iheight.show + ", width: " + iwidth.show) val embed = if (encoded.length < opts.math.embedLimit) then encoded else "" - //trace("embed: " + embed.length.show + ", " + opts.math.embedLimit.show) - val size = if (embed.isEmpty) then svg.length else embed.length + //trace("embed: " + embed.length.show + ", " + opts.math.embedLimit.show) + val size = if (embed.isEmpty) then svg.length else embed.length (iwidth,iheight,size,embed) } } @@ -523,11 +550,10 @@ val rxComment = regex(@"<!--[\s\S]*?-->|<\?xml[\s\S]*?\?>|\bversion='1.1'|\bxmln function svgExtractDim( svg : string, rx : regex ) : (double, string) { match(svg.find(rx)) { - Nothing -> (0.0, svg) + Nothing -> (0.0, svg) Just(cap) -> (dimension(cap.groups[1],cap.groups[2]), svg.substr(0,cap.index) + svg.substr(cap.next)) } } val rxSvgHeight = regex(@"\bheight='(\d+)(?:\.(\d+))?(pt)?'") val rxSvgWidth = regex(@"\bwidth='(\d+)(?:\.(\d+))?(pt)?'") - diff --git a/src/options.kk b/src/options.kk index f03891c7..e7fff113 100644 --- a/src/options.kk +++ b/src/options.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -17,7 +17,7 @@ import std/flags import std/regex import std/string import std/path -import common +import common // Metadata is just a string to string map. public alias metadata = list<(string,string)> @@ -34,43 +34,45 @@ public type bibstyle { Bst( styleName: string, locale: string ) Csl( styleName: string, locale: string ) } - + public struct mathoptions ( mode : mathmode = Static, // mode used to render math render : maybe<mathrender> = Nothing, // math rendering method renderFull: maybe<mathrender> = Nothing, // math rendering method for math-needfull snippets - + mathjax : string = "", // mathjax scripts path (use "default" for default path) mjext : string = "", // semicolon separated list of mathjax extensions - + imgDir : string = "math", // directory for static math images scale : int = 0, // math scaling in percentage - scalePng : int = 108, // default scaling for png + scalePng : int = 108, // default scaling for png scaleSvg : int = 105, // default scaling for svg - dpi : int = 300, // math resolution + dpi : int = 300, // math resolution baseline : int = 0, // math baseline offset in percentage of 1pt - embedLimit: int = 512*1024, // maximal embedded math image size + embedLimit: int = 512*1024, // maximal embedded math image size docClass : string = "[10pt]book", - + svgShare : bool = True, // share paths in embedded svg images svgPrec : int = 3, // decimal points used in svg rendering svgDefs : string = "", // used internally to share svg definitions svgFontFormat: string = "", // embed fonts directly in svg, use 'none' or '' to use SVG paths + concurrency : int = 4, // max concurrency for generating math renderings + dvipng : string = "dvipng", // used to extract png images for math - dvisvg : string = "dvisvgm", // used to extract svg for math + dvisvg : string = "dvisvgm", // used to extract svg for math latex : string = "", // if non-empty, used for math, use "" for the default program latexFull : string = "", // if non-empty, used for math with math-needfull. Use "" for default program dvips : string = "dvips", // used with ps2pdf when mathlatexFull is empty ps2pdf : string = "ps2pdf", convert : string = "convert", // used to extract png's from pdf for math that needs it - dim : dict<mathinfo> = dict() // static math dimension information + dim : dict<mathinfo> = dict() // static math dimension information ) // The options public struct options( - version : string = "", // Initialized to the program version + version : string = "", // Initialized to the program version bench : bool = False, // turn off more expensive features for benchmarking verbose : int = 0, // be more verbose verboseMaxLine : int = 78, // format lines to be no longer than 78 characters @@ -79,13 +81,13 @@ public struct options( xmp : bool = False, // only process markdown between xmp tags full : maybe<bool> = Nothing, // generate a full html/latex file instead of a snippet tex : bool = False, // generate latex file too - pdf : bool = False, // generate pdf file too + pdf : bool = False, // generate pdf file too texzip : bool = False, // generate tex zip for a submission rebuild : bool = False, // always rebuild bibtex, math etc. sandbox : bool = False, // run in a sandbox: only allow reading/writing from a subdirectory prelude : string = "prelude", // standard prelude definitions - - title : string = "", // html title + + title : string = "", // html title css : string = "madoko.css", // link to css style scripts : string = "", // link to javascript files scriptsx : string = "", // link to javascript files loaded at the end @@ -99,17 +101,17 @@ public struct options( texHeaderx: string= "", // literal tex header in non-math mode only texDocHeader: string= "", // literal tex header after begin{document} texDocHeaderx: string= "", // literal tex header after begin{document} in non-math mode only - texFooter: string= "", // literal tex footer + texFooter: string= "", // literal tex footer texSectionNum: bool = False, // use tex section numbering - + bibStyle : bibstyle = Csl("madoko-numeric",""), // bibliography style file (.bst or .csl) bib : string = "", // bibliography definition files (.bib) locale : string = "en-US", // language - + packages : string = "", // link to latex packages (.sty) packagesx: string = "", // packages in non-math mode only docClass : string = "", // latex document class (.cls) - + citestyle : maybe<citestyle> = Nothing, citeAll : bool = False, tocDepth : int = 3, // max. depth for inclusion in the table of contents @@ -120,7 +122,7 @@ public struct options( starBold : bool = False, // use * for bold? prettyAlign : int = 2, // default alignment spaces for .pretty mode logo : bool = True, // generate a logo at the end of the document - + math : mathoptions = Mathoptions(), highlight : bool = True, @@ -130,35 +132,35 @@ public struct options( bibtex : string = "bibtex", latex : string = "latex", // used to generate dvi for math processTimeout: int = 0, // kill child process after <timeout> - + zip : string = "zip", // zip program - + metadata : metadata = [], embedinfos : dict<embedinfo> = dict(), // embed data, used in a browser embedLimit : int = 512*1024, // limit for embedding data (javascript, css, etc.) lineNo : int = 1, // emit line no's. Use 0 to suppress. - lineNoWeb : bool = False, // emit line no info in html pages. + lineNoWeb : bool = False, // emit line no info in html pages. copyStyles : bool = True, // copy standard style files - lineMap : lineMap = End, // keep track of line numbers in include files + lineMap : lineMap = End, // keep track of line numbers in include files extractStart : string = @"^(?:\/\/|--|[#%]|[<]!--|\(\*) *BEGIN *: *(\w+) *(?:--[>]|\*\))?$", extractEnd : string = @"^(?:\/\/|--|[#%]|[<]!--|\(\*) *END *(?:[:] *(\w+) *)?(?:--[>]|\*\))?$" ); public function getPdfLatex( opts : options ) : string { - if (opts.pdflatex.bool) then opts.pdflatex + if (opts.pdflatex.bool) then opts.pdflatex else combine(opts.latex.dirname,"xelatex") -} +} public function getMathLatexFull( opts : options ) : string { if (opts.math.latexFull.bool) then opts.math.latexFull else opts.getMathLatex -} +} public function getMathLatex( opts : options ) : string { - if (opts.math.latex != "") then opts.math.latex + if (opts.math.latex != "") then opts.math.latex // elif (mrender.isPng) then opts.latex // dvi route is much faster for png extraction else opts.latex // was: opts.getPdfLatex, but pdflatex/latex proves more reliable for svg extraction -} +} public function getMathjax( opts : mathoptions ) : string { if (opts.mathjax != "default") then opts.mathjax @@ -220,7 +222,7 @@ function oflag( f : (options,bool) -> options ) : optionArg<commandOptions> function oreq( f : (options,string) -> options, help : string ) : optionArg<commandOptions> { - Req(fun(co:commandOptions,v:string) { co(options = f(co.options,v)) },help) + Req(fun(co:commandOptions,v:string) { co(options = f(co.options,v)) },help) } function setbench( o : options, b : bool ) { @@ -238,11 +240,11 @@ val optionsDesc : list<option<commandOptions>> Option( "", ["odir"], creq(fun(co,s) { co(outputDir=s) },"DIR"), "Write output files to the specified directory" ), Option( "", ["xmp"], oflag(fun(o,v) { o(xmp = v) }), "Only process markdown between <xmp> tags"), Option( "", ["tex"], oflag(fun(o,v) { o(tex = v) }), "Generate a LaTeX file too"), - Option( "", ["pdf"], oflag(fun(o,v) { o(pdf = v, tex = if (v) then True else o.tex) }), "Generate PDF using LaTeX"), + Option( "", ["pdf"], oflag(fun(o,v) { o(pdf = v, tex = if (v) then True else o.tex) }), "Generate PDF using LaTeX"), Option( "", ["texzip"], oflag(fun(o,v) { o(texzip = v, tex = if (v) then True else o.tex, pdf = if (v) then True else o.pdf, lineNo = 0) }), "Generate TeX zip for submission"), - Option( "", ["png"], oflag(fun(o,v) { o(math = (o.math)(render = Just(if (v) then Png else Svg))) }), "Use PNG instead of SVG for math in HTML"), + Option( "", ["png"], oflag(fun(o,v) { o(math = (o.math)(render = Just(if (v) then Png else Svg))) }), "Use PNG instead of SVG for math in HTML"), Option( "", ["convert-tex"], cflag(fun(co,v) { co(convertTex=v) }), "Convert input from TeX to Markdown"), - + Option( "f", ["fragment"], oflag(fun(o,v) { o(full=Just(!v)) }), "Generate a fragment instead of a full document"), Option( "", ["logo"], oflag(fun(o,v) { o(logo=v) }), "Generate a logo at the end of the document" ), Option( "", ["sanitize"], oflag(fun(o,v) { o(sanitize = v) }), "Always escape or suppress user defined html"), @@ -269,7 +271,7 @@ function setMeta( opts0 : options, value : string ) : options { else { warning("illegal --meta option: " + value) opts - } + } } } @@ -283,9 +285,9 @@ public function parseOptionList( version : string, cmdargs : list<string> ) : i { // testOptions.usageInfo( header ).println val (options,args,errs) = parse( CommandOptions(options = Options(version=version)), optionsDesc, cmdargs ) - + if (!(errs.isNil)) { - println( errs.join("\n") + "\n" + fullUsageInfo() ) + println( errs.join("\n") + "\n" + fullUsageInfo() ) Nothing } elif (options.showVersion) { @@ -295,7 +297,7 @@ public function parseOptionList( version : string, cmdargs : list<string> ) : i elif (args.isNil) { fullUsageInfo().println Nothing - } + } else { Just(options(inputs = args).check()) } @@ -307,7 +309,7 @@ function fullUsageInfo() { } // sanitize options -function check( cmdOptions : commandOptions ) : io commandOptions +function check( cmdOptions : commandOptions ) : io commandOptions { cmdOptions(installDir=if (cmdOptions.installDir=="") then programPath().dirname else cmdOptions.installDir) } @@ -322,7 +324,7 @@ public function getDocName( opts : options ) : string { function cutoff( s : string, n : int ) : string { if (n<=1) return s - s.lines.map(fun(line) { + s.lines.map(fun(line) { if (line.length >= n) then line.substr(0,n-1) + "\n" + line.substr(n-1) else line }).unlines } diff --git a/src/optionsSandbox.kk b/src/optionsSandbox.kk index 45a4bad1..8461f68f 100644 --- a/src/optionsSandbox.kk +++ b/src/optionsSandbox.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -15,7 +15,7 @@ import common import options -public function updateSandbox( options : options, key : string, lvalue : string, value : string, ivalue : int ) : options +public function updateSandbox( options : options, key : string, lvalue : string, value : string, ivalue : int ) : options { // can only set program options if not running in a sandbox if (!(options.sandbox)) { @@ -25,6 +25,7 @@ public function updateSandbox( options : options, key : string, lvalue : string, elif (key=="pdflatex" || key=="pdf-latex") options(pdflatex=if (lvalue=="true") then "default" else value) elif (key=="math-latex-full" || key=="math-pdflatex" || key=="math-pdf-latex") options(math = (options.math)(latexFull=if (lvalue=="true") then "default" else value)) elif (key=="math-latex") options(math = (options.math)(latex=if (lvalue=="true") then "default" else value)) + elif (key=="math-concurrency") options(math = (options.math)(concurrency=ivalue)) elif (key=="bibtex") options(bibtex=value) elif (key=="convert") options(math = (options.math)(convert=value)) elif (key=="ps2pdf") options(math = (options.math)(ps2pdf=value)) @@ -41,7 +42,7 @@ public function updateSandbox( options : options, key : string, lvalue : string, elif (key=="pdflatex" || key=="pdf-latex") options(pdflatex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.pdflatex) ) elif (key=="math-latex-full" || key=="math-pdflatex" || key=="math-pdf-latex") options(math = (options.math)(latexFull=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latexFull) )) elif (key=="math-latex") options(math = (options.math)(latex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latex) )) - elif (key=="bibtex") options(bibtex=safeBibtex(lvalue,options.bibtex)) + elif (key=="bibtex") options(bibtex=safeBibtex(lvalue,options.bibtex)) else { // if (options.verbose >= 3) warning("custom metadata key: " + key) options @@ -50,10 +51,10 @@ public function updateSandbox( options : options, key : string, lvalue : string, } function safeLatex( s : string, def : string ) { - if (s=="latex" || s=="xelatex" || s=="pdflatex") + if (s=="latex" || s=="xelatex" || s=="pdflatex") then s else def } function safeBibtex( s : string, def : string ) { - if (s=="bibtex" || s=="bibtex8") + if (s=="bibtex" || s=="bibtex8") then s else def } diff --git a/src/process-inline.js b/src/process-inline.js index 856aac7b..c267e910 100644 --- a/src/process-inline.js +++ b/src/process-inline.js @@ -1,7 +1,37 @@ /*--------------------------------------------------------------------------- Copyright 2013-2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. ---------------------------------------------------------------------------*/ + +function system_exec(cmds, callback, timeout, cwd, env) { + //js inline "child_process.exec(#1,{timeout:#3,cwd:(#4!=''?#4:undefined),env:(#5?(#5).unJust:undefined),windowsVerbatimArguments:true},function(err,stdout,stderr) { (#2)(err?err.code:0,stdout,stderr); });" + // console.log("systemx exec: " + cmds); + var options = { + timeout: timeout, + cwd: (cwd!=''?cwd:undefined), + env: (env?(env).unJust:undefined), + windowsVerbatimArguments: true, + }; + var allout = ""; + var allerr = ""; + var lasterr = 0; + var commands = cmds.split(";"); + var count = commands.length; + var i; + for(i = 0; i < count; i++) { + var cmd = commands[i]; + // console.log(" exec: " + cmd); + child_process.exec(cmd, options, function(err,stdout,stderr) { + allout += stdout + "\n"; + allerr += stderr + "\n"; + if (err) lasterr = err.code; + count--; + if (count<=0) { + (callback)( lasterr, allout, allerr); + } + }); + } +} diff --git a/src/process.kk b/src/process.kk index 041ce99b..d90d1e14 100644 --- a/src/process.kk +++ b/src/process.kk @@ -1,11 +1,11 @@ /*--------------------------------------------------------------------------- Copyright 2013 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. ---------------------------------------------------------------------------*/ -module process +module process import std/path import std/dict @@ -30,6 +30,5 @@ public function system( cmd : string, callback : (int,string,string) -> <io|e> ( external systemx( cmd : string, callback : (int,string,string) -> <io|e> (), timeout : int = 0, cwd : string = "", env : maybe<dict<string>> = Nothing ) : <io|e> () { cs inline "ShellCommand.system(#1,#2,#3,#4)" // TODO: environment - js inline "child_process.exec(#1,{timeout:#3,cwd:(#4!=''?#4:undefined),env:(#5?(#5).unJust:undefined),windowsVerbatimArguments:true},function(err,stdout,stderr) { (#2)(err?err.code:0,stdout,stderr); });" + js inline "system_exec(#1,#2,#3,#4,#5)" } - From 0f75f9f1d20bf93c757ccba912908b339368e036 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Fri, 14 Feb 2020 23:33:23 -0800 Subject: [PATCH 39/46] bump to version 1.1.9 --- package.json | 2 +- src/version.kk | 2 +- versionlog.json | 6 ++++++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0621e28c..d2393dc8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.8", + "version": "1.1.9", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/version.kk b/src/version.kk index 0bdbf462..bff08d41 100644 --- a/src/version.kk +++ b/src/version.kk @@ -10,4 +10,4 @@ module version // Do not edit the version, it is set in the Jakefile from package.json -public val version = "1.1.8" \ No newline at end of file +public val version = "1.1.9" \ No newline at end of file diff --git a/versionlog.json b/versionlog.json index 274a4f6b..343462fb 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,5 +1,11 @@ { "log" : [ + { "version": "1.1.9", + "date": "2020-02-14", + "updates": [ + "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")" + ] + }, { "version": "1.1.8", "date": "2020-02-08", "updates": [ From 5c877f0b4e2aba0ac9ed2e61a91cd081870a6766 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Fri, 14 Feb 2020 23:50:18 -0800 Subject: [PATCH 40/46] allow math concurrency setting outside sandbox --- src/optionsSandbox.kk | 1 + 1 file changed, 1 insertion(+) diff --git a/src/optionsSandbox.kk b/src/optionsSandbox.kk index 8461f68f..01a08102 100644 --- a/src/optionsSandbox.kk +++ b/src/optionsSandbox.kk @@ -42,6 +42,7 @@ public function updateSandbox( options : options, key : string, lvalue : string, elif (key=="pdflatex" || key=="pdf-latex") options(pdflatex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.pdflatex) ) elif (key=="math-latex-full" || key=="math-pdflatex" || key=="math-pdf-latex") options(math = (options.math)(latexFull=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latexFull) )) elif (key=="math-latex") options(math = (options.math)(latex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latex) )) + elif (key=="math-concurrency") options(math = (options.math)(concurrency=if(ivalue >= 1 && ivalue <= 8) then ivalue else options.math.concurrency) elif (key=="bibtex") options(bibtex=safeBibtex(lvalue,options.bibtex)) else { // if (options.verbose >= 3) warning("custom metadata key: " + key) From f36a7ac250fbf2c59949cf219ce7b28d58f47e3b Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Fri, 14 Feb 2020 23:50:33 -0800 Subject: [PATCH 41/46] update madoko local --- support/madoko-local/Jakefile.js | 24 +++++++++++----------- support/madoko-local/package.json | 2 +- support/madoko-local/readme.md | 4 ++-- support/madoko-local/src/config.js | 2 +- support/madoko-local/src/run.js | 30 ++++++++++++++-------------- support/madoko-local/src/util.js | 2 +- support/madoko-local/versionlog.json | 6 ++++++ 7 files changed, 38 insertions(+), 32 deletions(-) diff --git a/support/madoko-local/Jakefile.js b/support/madoko-local/Jakefile.js index 7ec5d3fd..cbfb39ba 100644 --- a/support/madoko-local/Jakefile.js +++ b/support/madoko-local/Jakefile.js @@ -17,10 +17,10 @@ var main = "madoko-local"; var sourceDir = "src"; var mainCli = Path.join(sourceDir,"cli.js"); var nodeExe = "node"; -var defaultArgs = "--port=81 --verbose=2" +var defaultArgs = "--port=8080 --run --verbose=2" //----------------------------------------------------- -// Tasks: compilation +// Tasks: compilation //----------------------------------------------------- task("default",["server"]); @@ -31,8 +31,8 @@ task("server", [], function() { fixVersion(); var cmd = [nodeExe,mainCli,defaultArgs,args].join(" "); jake.logger.log("> " + cmd); - jake.exec(cmd, {interactive: true}, function() { - complete(); + jake.exec(cmd, {interactive: true}, function() { + complete(); }); },{async:true}); @@ -56,7 +56,7 @@ var usageInfo = [ function showHelp() { jake.logger.log(usageInfo); jake.showAllTaskDescriptions(jake.program.opts.tasks); - process.exit(); + process.exit(); } desc("show this information"); @@ -85,12 +85,12 @@ function getVersion() { } } return "<unknown>" -} +} function fixVersion(fname) { fname = fname || Path.join(sourceDir,"config.js"); - + var config = { version: getVersion(), main : main, @@ -108,14 +108,14 @@ function fixVersion(fname) { } if (content !== content0) { Fs.writeFileSync(fname,content,{encoding: "utf8"}); - } + } } } function fileExist(fileName) { var stats = null; try { - stats = Fs.statSync(fileName); + stats = Fs.statSync(fileName); } catch(e) {}; return (stats != null); @@ -126,12 +126,12 @@ function fileExist(fileName) { function copyFiles(rootdir,files,destdir) { rootdir = rootdir || ""; rootdir = rootdir.replace(/\\/g, "/"); - jake.mkdirP(destdir); + jake.mkdirP(destdir); files.forEach(function(filename) { // make relative var destname = Path.join(destdir,(rootdir && filename.lastIndexOf(rootdir,0)===0 ? filename.substr(rootdir.length) : filename)); - var logfilename = (filename.length > 30 ? "..." + filename.substr(filename.length-30) : filename); - var logdestname = (destname.length > 30 ? "..." + destname.substr(destname.length-30) : destname); + var logfilename = (filename.length > 30 ? "..." + filename.substr(filename.length-30) : filename); + var logdestname = (destname.length > 30 ? "..." + destname.substr(destname.length-30) : destname); //jake.logger.log("cp -r " + logfilename + " " + logdestname); jake.cpR(filename,Path.dirname(destname)); }) diff --git a/support/madoko-local/package.json b/support/madoko-local/package.json index 54399b7a..fadf8288 100644 --- a/support/madoko-local/package.json +++ b/support/madoko-local/package.json @@ -1,7 +1,7 @@ { "name": "madoko-local", "author": "Daan Leijen, Microsoft Research", - "version": "0.9.2", + "version": "0.9.3", "homepage": "http://madoko.codeplex.com", "description": "Madoko-Local provides local disk access to Madoko.net", "licenses": [ diff --git a/support/madoko-local/readme.md b/support/madoko-local/readme.md index 869da77c..b3708174 100644 --- a/support/madoko-local/readme.md +++ b/support/madoko-local/readme.md @@ -29,7 +29,7 @@ sub-directories will be accessible to Madoko. Here we run it with access to the current directory: ``` > madoko-local -l . -listening on : http://localhost +listening on : http://localhost:8080 connecting securely to : https://www.madoko.net serving files under : C:\Users\dknuth\docs @@ -43,7 +43,7 @@ secure https with the Madoko website, and which local directory is accessible within Madoko. The `-l` flag will launch the browser and go to the listed url, i.e. -`http://localhost#secret=OsuwK3HbMoI7` in our example. The 'secret' in +`http://localhost:8080#secret=OsuwK3HbMoI7` in our example. The 'secret' in the url is unique on each computer and used as an extra level of security. diff --git a/support/madoko-local/src/config.js b/support/madoko-local/src/config.js index 303c9c70..56f32cf5 100644 --- a/support/madoko-local/src/config.js +++ b/support/madoko-local/src/config.js @@ -9,7 +9,7 @@ if (typeof define !== 'function') { var define = require('amdefine')(module) } define([],function() { -var version = "0.9.2"; +var version = "0.9.3"; var main = "madoko-local"; return { diff --git a/support/madoko-local/src/run.js b/support/madoko-local/src/run.js index d90df06e..4aa31a0d 100644 --- a/support/madoko-local/src/run.js +++ b/support/madoko-local/src/run.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -18,7 +18,7 @@ var Sandbox = require("./sandbox.js"); // ------------------------------------------------------------- -// Helpers +// Helpers // ------------------------------------------------------------- // this routine replace parent directory references (..) by a .parent directory @@ -33,7 +33,7 @@ function xnormalize(fpath) { } else if (part==="..") { if (roots.length > 0 && roots[roots.length-1] !== ".parent") { - roots.pop(); + roots.pop(); } else { roots.push(".parent"); @@ -75,14 +75,14 @@ var Target = { }; // Read madoko generated files. -// config: -// limits.fileSize : int +// config: +// limits.fileSize : int // mime.lookup(path) function readFiles( config, userpath, docname, target, out ) { var ext = Path.extname(docname); var stem = docname.substr(0, docname.length - ext.length ); - var fnames = [".dimx", "-math-plain.dim", "-math-full.dim", - "-math-plain.tex", "-math-full.tex", + var fnames = [".dimx", "-math-plain.dim", "-math-full.dim", + "-math-plain.tex", "-math-full.tex", "-math-plain.final.tex", "-math-full.final.tex", "-bib.bbl", "-bib.final.aux", // todo: handle multiple bibliographies "-bib.bbl.mdk", "-bib.bib.json", @@ -90,7 +90,7 @@ function readFiles( config, userpath, docname, target, out ) { .concat( target>Target.Math ? [".pdf",".tex"] : [] ) .concat( target===Target.TexZip ? [".zip"] : [] ) .map( function(s) { return Util.combine( outdir, stem + s ); }) - .concat( target>Target.Math ? [Util.combine(outdir,"madoko2.sty")] : [] ); + .concat( target>Target.Math ? [Util.combine(outdir,"madoko2.sty")] : [] ); // find last log file var rxLog = /^[ \t]*log written at: *([\w\-\/\\]+\.log) *$/mig; var cap = rxLog.exec(out); @@ -140,7 +140,7 @@ function readFiles( config, userpath, docname, target, out ) { } ); }); - })); + })); } // execute madoko program @@ -150,7 +150,7 @@ function madokoExec( program, userpath, docname, flags, extraflags, timeout ) { return new Promise( function(cont) { Log.message("> " + command); Cp.exec( command, {cwd: userpath, timeout: timeout || 10000, maxBuffer: 512*1024 }, cont); - }); + }); } // Run madoko program @@ -165,10 +165,10 @@ function madokoRunIn( config, userpath, docname, files, target ) { return saveFiles( userpath, files ).then( function() { Sandbox.getSafePath(userpath,docname); // is docname safe? var tgtflag = (target===Target.Pdf ? " --pdf" : (target===Target.TexZip ? " --texzip" : "")); - var flags = "-vv --verbose-max=0 -mmath-embed:512 -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; + var flags = "-vv --verbose-max=0 -mmath-embed:512 -mmath-concurrency:8 -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; var extraflags = config.runflags || ""; - var timeout = (target>Target.Math ? config.limits.timeoutPDF : config.limits.timeoutMath); - var startTime = Date.now(); + var timeout = (target>Target.Math ? config.limits.timeoutPDF : config.limits.timeoutMath); + var startTime = Date.now(); return madokoExec( config.run, userpath, docname, flags, extraflags, timeout ).then( function(stdout,stderr) { var endTime = Date.now(); var out = stdout + "\n" + stderr + "\n"; @@ -183,7 +183,7 @@ function madokoRunIn( config, userpath, docname, files, target ) { }); }, function(err,stdout,stderr) { Log.info("madoko failed: \nstdout: " + stdout + "\nstderr: " + stderr + "\n"); - if (err) Log.info(err.toString()); + if (err) Log.info(err.toString()); err.stdout = stdout; err.stderr = stderr; throw err; @@ -228,4 +228,4 @@ return { madokoRun: madokoRun, }; -}); \ No newline at end of file +}); diff --git a/support/madoko-local/src/util.js b/support/madoko-local/src/util.js index 63f01151..a032b387 100644 --- a/support/madoko-local/src/util.js +++ b/support/madoko-local/src/util.js @@ -151,7 +151,7 @@ function ensureDir(dir) { // remove everything in dir recursively function removeDirAll(dir) { - return new Promise( function(cont) { return rmdirRF(dir,{maxRetries:5},cont); } + return new Promise( function(cont) { return rmdirRF(dir,{maxRetries:5},cont); }); } // remove a directory if it is empty diff --git a/support/madoko-local/versionlog.json b/support/madoko-local/versionlog.json index 59172f0d..89e8ac0e 100644 --- a/support/madoko-local/versionlog.json +++ b/support/madoko-local/versionlog.json @@ -1,5 +1,11 @@ { "log": [ + { "version": "0.9.3", + "date" : "2020-02-14", + "updates": [ + "More liberal timeouts" + ] + }, { "version": "0.9.1", "date" : "2020-02-05", "updates": [ From ee8ac1ae2265767db358a16670908a3fb514fc7d Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Fri, 14 Feb 2020 23:51:53 -0800 Subject: [PATCH 42/46] fix build --- package.json | 2 +- src/optionsSandbox.kk | 2 +- src/version.kk | 2 +- versionlog.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index d2393dc8..1ee70ea8 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.9", + "version": "1.1.10", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/optionsSandbox.kk b/src/optionsSandbox.kk index 01a08102..fddece11 100644 --- a/src/optionsSandbox.kk +++ b/src/optionsSandbox.kk @@ -42,7 +42,7 @@ public function updateSandbox( options : options, key : string, lvalue : string, elif (key=="pdflatex" || key=="pdf-latex") options(pdflatex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.pdflatex) ) elif (key=="math-latex-full" || key=="math-pdflatex" || key=="math-pdf-latex") options(math = (options.math)(latexFull=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latexFull) )) elif (key=="math-latex") options(math = (options.math)(latex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latex) )) - elif (key=="math-concurrency") options(math = (options.math)(concurrency=if(ivalue >= 1 && ivalue <= 8) then ivalue else options.math.concurrency) + elif (key=="math-concurrency") options(math = (options.math)(concurrency=if(ivalue >= 1 && ivalue <= 8) then ivalue else options.math.concurrency)) elif (key=="bibtex") options(bibtex=safeBibtex(lvalue,options.bibtex)) else { // if (options.verbose >= 3) warning("custom metadata key: " + key) diff --git a/src/version.kk b/src/version.kk index bff08d41..cf841ebc 100644 --- a/src/version.kk +++ b/src/version.kk @@ -10,4 +10,4 @@ module version // Do not edit the version, it is set in the Jakefile from package.json -public val version = "1.1.9" \ No newline at end of file +public val version = "1.1.10" \ No newline at end of file diff --git a/versionlog.json b/versionlog.json index 343462fb..feb6d355 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,6 +1,6 @@ { "log" : [ - { "version": "1.1.9", + { "version": "1.1.10", "date": "2020-02-14", "updates": [ "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")" From 572cae41a768bc274d72535a6c69fd699caf1633 Mon Sep 17 00:00:00 2001 From: daan <daanl@outlook.com> Date: Sat, 15 Feb 2020 00:10:22 -0800 Subject: [PATCH 43/46] 2 minute process timeout in sandbox mode --- package.json | 2 +- src/driver.kk | 112 ++++++++++++++++---------------- src/optionsSandbox.kk | 4 +- support/madoko-local/src/run.js | 2 +- versionlog.json | 5 +- 5 files changed, 63 insertions(+), 62 deletions(-) diff --git a/package.json b/package.json index 1ee70ea8..757fa2ae 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.10", + "version": "1.1.11", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/driver.kk b/src/driver.kk index 4f9b031d..5c12881d 100644 --- a/src/driver.kk +++ b/src/driver.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013-2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -37,10 +37,10 @@ public struct runners( val standardTexStyles = ["ellipse.sty","options.sty","longbox.sty","longfbox.sty","madoko2.sty"] -public function processContent( inName : string, outName : string, content : string, +public function processContent( inName : string, outName : string, content : string, opts : commandOptions, firstTime : bool = False, runners : runners, - continue : (output:string,inputName:string,outputName:string,options:options) -> io () ) : io () + continue : (output:string,inputName:string,outputName:string,options:options) -> io () ) : io () { if (opts.options.sandbox) enforceSandbox() if (opts.convertTex) { @@ -57,11 +57,11 @@ public function processContent( inName : string, outName : string, content : str // remove madoko comments val icontent = icontent0.removeMadokoComments // set up options - - val date = now() - val opts1 = opts0(lineMap=lmap, - processTimeout=if (opts0.sandbox) then 60000 else opts0.processTimeout, - metadata=opts0.metadata + + val date = now() + val opts1 = opts0(lineMap=lmap, + processTimeout=if (opts0.sandbox) then 120000 else opts0.processTimeout, + metadata=opts0.metadata + [("docname",inName.stemname),("filename",inName)] + [("madoko-version",opts0.version)] + [("date",date.isoLocalDate),("time",date.isoLocalTime.substr(0,5)), @@ -71,7 +71,7 @@ public function processContent( inName : string, outName : string, content : str // extract default cite-style if it exists val opts2 = opts1.extractCitestyle(outName) - val mmopts = if (opts2.xmp) + val mmopts = if (opts2.xmp) then opts2.parseMeta( FmtHtml, icontent.normalizeSource.extractFirstXmp ).fst else opts2.parseMeta( FmtHtml, icontent.normalizeSource ).fst // get bibdata,bibstyle,mathimg @@ -92,24 +92,24 @@ public function processContent( inName : string, outName : string, content : str // } if (xopts.tex || mopts.math.mode.isStatic) { // only copy style files if generating tex or if math is static val isTex2 = match(mopts.metadata.find( fun(md) { md.fst == "tex2" } )) { - Just(md) -> md.snd.toLower == "true" + Just(md) -> md.snd.toLower == "true" _ -> False } val files = if (isTex2) then standardTexStyles - else ["css.sty","madoko.sty"] - files.foreach fun(fname) { + else ["css.sty","madoko.sty"] + files.foreach fun(fname) { tryCopyTextFileFromNoSandboxTo(fname, styleDir, outName.dirname ) } } } - process( inName, outName, searchDirs, content, icontent, xopts, mopts, firstTime, runners) + process( inName, outName, searchDirs, content, icontent, xopts, mopts, firstTime, runners) fun() { processContent(inName,outName,content,opts,False,runners,continue) // redo! } fun(html) { - continue(html,inName,outName,mopts) + continue(html,inName,outName,mopts) } } } @@ -125,23 +125,23 @@ val rxXmp = regex(@"<xmp\b.*?>([\s\S]*?)") val rxCurDir = regex(@"^(\.)(?=[\\/])") function process( inName : string, outName : string, searchDirs : list, - content : string, icontent : string, + content : string, icontent : string, xopts0 : options, mopts : options, firstTime : bool, runners : runners, redo : () -> io (), - continue : (string) -> io () ) : io () + continue : (string) -> io () ) : io () { function genTexZip(outTexName:string, refers:list) { if (mopts.texzip) { - createTexZip( inName, outTexName, refers, mopts, runners, fun(err) { () } ) + createTexZip( inName, outTexName, refers, mopts, runners, fun(err) { () } ) } } function genPdf(outTexName:string, refers:list) { if (mopts.pdf) { - (runners.runPdfLatex)( inName, outTexName, mopts, content, fun(err) { + (runners.runPdfLatex)( inName, outTexName, mopts, content, fun(err) { if (err==0) then genTexZip(outTexName, refers) else () - }) - } + }) + } else () // cannot zip without pdf } @@ -150,7 +150,7 @@ function process( inName : string, outName : string, searchDirs : list, val infos = files.concat fun(fname) { val mime = mimeFromExt(fname); - val data = if (mime.startsWith("text/")) + val data = if (mime.startsWith("text/")) then searchReadTextFileDef(fname,"",searchDirs,"",False) elif (mime.startsWith("image/")) then { @@ -161,7 +161,7 @@ function process( inName : string, outName : string, searchDirs : list, else "" // write the file to the out directory - if (data != "" && (data.length > mopts.embedLimit)) { + if (data != "" && (data.length > mopts.embedLimit)) { val newName = combine(outName.dirname,fname) // assumes just relative embed names! if (newName.normalize != fname.normalize) { //trace("copying: " + fname + " to " + newName) @@ -174,36 +174,36 @@ function process( inName : string, outName : string, searchDirs : list, } () } - } - + } + // embed - if (data != "" && data.length < mopts.embedLimit) + if (data != "" && data.length < mopts.embedLimit) then [(fname,Embedinfo(fname,data))] else { if (fname!="" && mopts.embedLimit > 0) { - mopts.printErr("warning: unable to embed: " + fname + + mopts.printErr("warning: unable to embed: " + fname + (if (data.length > 0) then " (too large)" else " (does not exist)")) } [] } } if (infos.length == 0) return (opts,html0) - + val newopts = opts(embedinfos = opts.embedinfos + infos.dict, verbose=min(opts.verbose,1)) val newhtml = markdown(icontent,newopts).fst (newopts,newhtml) } - function phaseHtml() + function phaseHtml() { - // markdown to html - val (fileEmbed,(mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions)))))) = + // markdown to html + val (fileEmbed,(mathPlain,(mathFull,(warns0,(logs, (html0,_fullOptions)))))) = withLog("embed") { withLogCompress("math-plain") { withLogCompress("math-full") { - withLogCompress("warning") { - withLogCompress("aux") { - markdown(icontent,xopts0) + withLogCompress("warning") { + withLogCompress("aux") { + markdown(icontent,xopts0) }}}}} // write an aux file val bibChanged = @@ -213,7 +213,7 @@ function process( inName : string, outName : string, searchDirs : list, if (mopts.bibStyle.extname != "" || mopts.bibStyle.dirname != "") { // copy bst file tryCopyTextFileFromTo( mopts.bibStyle.basename, mopts.bibStyle.dirname, outName.dirname ) - () + () } */ writeCitationsData( logs, outName, searchDirs, mopts.bib.splitPaths, mopts ) @@ -234,11 +234,11 @@ function process( inName : string, outName : string, searchDirs : list, } else { // do embedding - val (xopts,html) = embedFiles(xopts0,fileEmbed.split("\n").list,html0); + val (xopts,html) = embedFiles(xopts0,fileEmbed.split("\n").list,html0); // write html result if (!(tryWriteTextFile(outName,html))) { - mopts.printErr("error: unable to write: " + outName) + mopts.printErr("error: unable to write: " + outName) } // show warnings @@ -252,13 +252,13 @@ function process( inName : string, outName : string, searchDirs : list, // generate tex val outTexName = outName.changeExt(".tex") - val texRefers = + val texRefers = if (!mopts.tex) then [] else { - if (mopts.verbose >= 1) println("process: " + inName + " -> " + outTexName ) - val (texwarns, (texFiles, (texRefer, tex))) + if (mopts.verbose >= 1) println("process: " + inName + " -> " + outTexName ) + val (texwarns, (texFiles, (texRefer, tex))) = withLog("texwarning", { withLog("files", { - withLog("filesRefer", { + withLog("filesRefer", { markdown(icontent,xopts,FmtTex).fst }) }) @@ -266,11 +266,11 @@ function process( inName : string, outName : string, searchDirs : list, if (texwarns != "") { log("stdout",texwarns) if (mopts.verbose>=1) { - print(texwarns) + print(texwarns) } } if (!(tryWriteTextFile(outTexName,tex))) { - mopts.printErr("error: unable to write: " + outTexName) + mopts.printErr("error: unable to write: " + outTexName) } texFiles.split("\n").list + texRefer.split("\n").list } @@ -285,10 +285,10 @@ function process( inName : string, outName : string, searchDirs : list, Just((mdim2,svgdefs)) -> { // write html again mopts.print("re-aligning math in HTML.") - + val html2 = markdown(icontent,xopts(math=(xopts.math)(dim=mdim2,svgDefs=svgdefs))).fst if (!(tryWriteTextFile(outName,html2))) { - mopts.printErr("error: unable to write: " + outName) + mopts.printErr("error: unable to write: " + outName) } } Nothing -> () @@ -296,12 +296,12 @@ function process( inName : string, outName : string, searchDirs : list, //if (mopts.verbose>=1) println(" done typesetting math.") genPdf(outTexName,texRefers) } - } + } else { genPdf(outTexName,texRefers) - } + } - continue(html) + continue(html) } } @@ -339,7 +339,7 @@ function fixWarnings(txt : string) : string { Just(cap) -> { val count = hist[line].mbint hist[line] := count+1 - if (count == 4) + if (count == 4) then Just("warning: " + location + " ignoring from now on:" + line + "\n") elif (count > 4) then Nothing @@ -392,23 +392,23 @@ function registerColorizers( opts : options, searchDirs : list, content } } } - } + } } public function outputName( inputName : string, options : commandOptions ) : string { - val noextName = if (inputName.endsWith(".xmp.html")) then inputName.substr(0,inputName.length-9) else inputName.noext + val noextName = if (inputName.endsWith(".xmp.html")) then inputName.substr(0,inputName.length-9) else inputName.noext val outName = if (options.convertTex) then inputName + ".mdk" else noextName + ".html" - if (options.outputDir=="") - then outName - else options.outputDir + "/" + outName.basename + if (options.outputDir=="") + then outName + else options.outputDir + "/" + outName.basename } function tryCopyTextFileFromNoSandboxTo( fname, srcDir, outDir ) { val inName = combine(srcDir,fname) val outName = combine(outDir,fname) - catch { + catch { val txt = readTextFileNoSandbox( inName ) - tryWriteTextFile(outName,txt) + tryWriteTextFile(outName,txt) () } fun(exn) { () } @@ -420,4 +420,4 @@ public function withLogCompress( name: string, action : () -> a ) : =0 && ivalue<=256) then ivalue else options.math.concurrency)) elif (key=="bibtex") options(bibtex=value) elif (key=="convert") options(math = (options.math)(convert=value)) elif (key=="ps2pdf") options(math = (options.math)(ps2pdf=value)) @@ -42,7 +42,7 @@ public function updateSandbox( options : options, key : string, lvalue : string, elif (key=="pdflatex" || key=="pdf-latex") options(pdflatex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.pdflatex) ) elif (key=="math-latex-full" || key=="math-pdflatex" || key=="math-pdf-latex") options(math = (options.math)(latexFull=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latexFull) )) elif (key=="math-latex") options(math = (options.math)(latex=if (lvalue=="true") then "default" else safeLatex(lvalue, options.math.latex) )) - elif (key=="math-concurrency") options(math = (options.math)(concurrency=if(ivalue >= 1 && ivalue <= 8) then ivalue else options.math.concurrency)) + elif (key=="math-concurrency") options(math = (options.math)(concurrency=if(ivalue >= 1 && ivalue <= 16) then ivalue else options.math.concurrency)) elif (key=="bibtex") options(bibtex=safeBibtex(lvalue,options.bibtex)) else { // if (options.verbose >= 3) warning("custom metadata key: " + key) diff --git a/support/madoko-local/src/run.js b/support/madoko-local/src/run.js index 4aa31a0d..ad6f49b4 100644 --- a/support/madoko-local/src/run.js +++ b/support/madoko-local/src/run.js @@ -165,7 +165,7 @@ function madokoRunIn( config, userpath, docname, files, target ) { return saveFiles( userpath, files ).then( function() { Sandbox.getSafePath(userpath,docname); // is docname safe? var tgtflag = (target===Target.Pdf ? " --pdf" : (target===Target.TexZip ? " --texzip" : "")); - var flags = "-vv --verbose-max=0 -mmath-embed:512 -mmath-concurrency:8 -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; + var flags = "-vv --verbose-max=0 -mmath-embed:512 -mmath-concurrency:1 -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; var extraflags = config.runflags || ""; var timeout = (target>Target.Math ? config.limits.timeoutPDF : config.limits.timeoutMath); var startTime = Date.now(); diff --git a/versionlog.json b/versionlog.json index feb6d355..091e749c 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,9 +1,10 @@ { "log" : [ - { "version": "1.1.10", + { "version": "1.1.11", "date": "2020-02-14", "updates": [ - "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")" + "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")", + "Allow 2 minute process timeout in sandbox mode", ] }, { "version": "1.1.8", From 6d582ddda14c8b519b60198144b500b815e86a99 Mon Sep 17 00:00:00 2001 From: daan Date: Sat, 15 Feb 2020 00:33:08 -0800 Subject: [PATCH 44/46] update madoko local with concurrency flag --- package.json | 2 +- src/version.kk | 2 +- support/madoko-local/package.json | 2 +- support/madoko-local/readme.md | 14 ++++++++-- support/madoko-local/src/config.js | 2 +- support/madoko-local/src/init.js | 5 ++++ support/madoko-local/src/main.js | 40 ++++++++++++++-------------- support/madoko-local/src/run.js | 3 ++- support/madoko-local/versionlog.json | 5 ++-- versionlog.json | 2 +- 10 files changed, 47 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 757fa2ae..74f93580 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.11", + "version": "1.1.12", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/version.kk b/src/version.kk index cf841ebc..6bfe6519 100644 --- a/src/version.kk +++ b/src/version.kk @@ -10,4 +10,4 @@ module version // Do not edit the version, it is set in the Jakefile from package.json -public val version = "1.1.10" \ No newline at end of file +public val version = "1.1.12" \ No newline at end of file diff --git a/support/madoko-local/package.json b/support/madoko-local/package.json index fadf8288..5920b91b 100644 --- a/support/madoko-local/package.json +++ b/support/madoko-local/package.json @@ -1,7 +1,7 @@ { "name": "madoko-local", "author": "Daan Leijen, Microsoft Research", - "version": "0.9.3", + "version": "0.9.4", "homepage": "http://madoko.codeplex.com", "description": "Madoko-Local provides local disk access to Madoko.net", "licenses": [ diff --git a/support/madoko-local/readme.md b/support/madoko-local/readme.md index b3708174..6705d7bb 100644 --- a/support/madoko-local/readme.md +++ b/support/madoko-local/readme.md @@ -11,6 +11,8 @@ standard cloud storage (like Dropbox) or are already using a particular Git repository but still want to have the rich editing experience provided by Madoko.net. +It is also useful to render all LaTeX on a local machine. + # Installation Ensure you have [Node.js] installed on your system. When that @@ -28,10 +30,11 @@ like to access as an argument. Everything in that directory, and all its sub-directories will be accessible to Madoko. Here we run it with access to the current directory: ``` -> madoko-local -l . +> madoko-local -r -l . listening on : http://localhost:8080 connecting securely to : https://www.madoko.net serving files under : C:\Users\dknuth\docs +running madoko using : madoko (concurrency: 4) --------------------------------------------------------------- access server at : http://localhost#secret=OsuwK3HbMoI7 @@ -42,6 +45,10 @@ not accept connections from outside. It also shows that it connects using secure https with the Madoko website, and which local directory is accessible within Madoko. +The `-r` flag also runs Madoko on the local machine to render math or PDF +using LaTeX (without `-r` this is done on the madoko.net server which +may be slower). + The `-l` flag will launch the browser and go to the listed url, i.e. `http://localhost:8080#secret=OsuwK3HbMoI7` in our example. The 'secret' in the url is unique on each computer and used as an extra level of @@ -106,7 +113,10 @@ Options: : Specify the user home directory. In this directory `madoko-local` will create a `.madoko` directory that contains log files and the local configuration - file (`config.json`). + file (`config.json`). +* `-c`, `--concurrency=` + : Run madoko with the specified concurrency to speed up + svg rendering for mathematics. * `--origin=` : Instead of serving `https://www.madoko.net` use the specified `url`. Only specify trusted websites here diff --git a/support/madoko-local/src/config.js b/support/madoko-local/src/config.js index 56f32cf5..857294c7 100644 --- a/support/madoko-local/src/config.js +++ b/support/madoko-local/src/config.js @@ -9,7 +9,7 @@ if (typeof define !== 'function') { var define = require('amdefine')(module) } define([],function() { -var version = "0.9.3"; +var version = "0.9.4"; var main = "madoko-local"; return { diff --git a/support/madoko-local/src/init.js b/support/madoko-local/src/init.js index cc520b3b..692d4dc3 100644 --- a/support/madoko-local/src/init.js +++ b/support/madoko-local/src/init.js @@ -47,6 +47,7 @@ var config = { origin : "https://www.madoko.net", secret : null, verbose : 0, + concurrency: 4, limits : { fileSize : 64*mb, logFlush : 5*minute, @@ -77,6 +78,7 @@ Options .option("--runflags ", "pass extra options to the madoko program") .option("--verbose [n]","output trace messages (0 none, 1 info, 2 debug)", parseInt ) .option("--rmdelay ","delay before run directory is removed", parseInt ) + .option("-c, --concurrency ", "render math using (=" + config.concurrency.toString() + ") processors", parseInt ) Options.on("--help", function() { console.log([ @@ -127,6 +129,9 @@ function initializeConfig() { if (Options.port) config.port = Options.port; else if (typeof localConfig.port === "number") config.port = localConfig.port; + // Concurrency + if (Options.concurrency) config.concurrency = Options.concurrency; + // Origin if (Options.origin) config.origin = Options.origin; else if (typeof localConfig.origin === "string") config.origin = localConfig.origin; diff --git a/support/madoko-local/src/main.js b/support/madoko-local/src/main.js index eb5647a9..2bb7a748 100644 --- a/support/madoko-local/src/main.js +++ b/support/madoko-local/src/main.js @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -32,20 +32,20 @@ var localHostIP = "127.0.0.1"; var config = Init.initializeConfig(); // ------------------------------------------------------------- -// Set up server app +// Set up server app // ------------------------------------------------------------- var app = App.createServer(config.limits.fileSize); config.mime = app.locals.mime; // set extra mime types -app.locals.mime.define({ +app.locals.mime.define({ "text/madoko": ["mdk"], "text/markdown": ["md","mkdn","markdown"], "text/plain": ["tex","sty","cls","bib","bbl","aux","dimx","dim","csl","bst"], }); // ------------------------------------------------------------- -// Security +// Security // ------------------------------------------------------------- app.use(function(req, res, next){ @@ -55,7 +55,7 @@ app.use(function(req, res, next){ throw new Util.HttpError( "unauthorized access; secret key is not correct.", 401 ); } } - + // extra check: only serve to local host if (req.ip !== req.connection.remoteAddress || req.ip !== localHostIP) { throw new Util.HttpError( "only serving localhost", 403 ); @@ -64,7 +64,7 @@ app.use(function(req, res, next){ // check mount directory matches if (config.mountdir && req.query && req.query.mount) { if (!Util.pathIsEqual(req.query.mount, config.mountdir)) { - throw new Util.HttpError( + throw new Util.HttpError( ["Document was previously served from a different local root directory!", " Previous root: " + req.query.mount, " Current root : " + config.mountdir].join("\n"), 403 ); @@ -75,7 +75,7 @@ app.use(function(req, res, next){ }); // Use content security policy; very safe by default. -App.useCSP(app, { +App.useCSP(app, { "default-src": "'self'", "connect-src": "'self'", "style-src" : "'self' 'unsafe-inline'", // mostly for chrome-extensions :-( @@ -95,15 +95,15 @@ function finfoFromStat( stat, fpath ) { finfo = null; } else { - finfo = { + finfo = { bytes: stat.size, - modified: stat.mtime.toISOString(), + modified: stat.mtime.toISOString(), is_dir: stat.isDirectory(), path: fpath, contents: [], }; }; - return finfo; + return finfo; } @@ -112,12 +112,12 @@ function getLocalPath(fpath) { } // ------------------------------------------------------------- -// Initial page +// Initial page // ------------------------------------------------------------- function getConfig(req,res) { if (req.query.show) { - Log.message("\nlocally host madoko to: " + req.connection.remoteAddress + " (" + req.hostname + ")\n" + + Log.message("\nlocally host madoko to: " + req.connection.remoteAddress + " (" + req.hostname + ")\n" + "serving files under : " + config.mountdir + "\n"); } res.send( { @@ -150,7 +150,7 @@ function getMetadata(req,res) { finfo.contents.push( finfoFromStat(stats[i], Util.combine(relpath,files[i])) ); } } - Log.trace("dir listing: " + finfo.path + ": " + finfo.contents.length.toString() + " items."); + Log.trace("dir listing: " + finfo.path + ": " + finfo.contents.length.toString() + " items."); return finfo; }); }); @@ -180,7 +180,7 @@ function getReadFile(req,res) { function putWriteFile(req,res) { Log.info("write file : " + req.query.path); - var fpath = getLocalPath(req.query.path); + var fpath = getLocalPath(req.query.path); var rtime = (typeof req.query.remoteTime === "string" ? Util.dateFromISOString(req.query.remoteTime) : null); return Util.fstat( fpath ).then( function(stat) { if (stat && rtime) { @@ -198,8 +198,8 @@ function putWriteFile(req,res) { return Util.fstat(fpath).then( function(stat) { if (!stat) throw new Util.HttpError( "File could not be saved"); Log.trace("file write : final mtime: " + stat.mtime.toISOString()); - res.send({ - path: req.query.path, + res.send({ + path: req.query.path, modified: stat.mtime.toISOString(), }); }); @@ -252,11 +252,11 @@ App.entries( app, { "PUT/rest/writefile": putWriteFile, "POST/rest/createfolder" : postCreateFolder, "POST/rest/run" : postRun, - "POST/report/csp" : cspReport, + "POST/report/csp" : cspReport, }); // ------------------------------------------------------------- -// Static content +// Static content // ------------------------------------------------------------- App.serveStatic(app, Util.combine(config.installdir, "static") ); @@ -268,7 +268,7 @@ App.serveStatic(app, Util.combine(config.installdir, "static") ); App.handleErrors(app); // ------------------------------------------------------------- -// Start listening +// Start listening // ------------------------------------------------------------- Http.createServer(app).listen(config.port, "localhost"); // only listen on local host @@ -279,7 +279,7 @@ var accessPoint = localHost + (config.secret ? "#secret=" + encodeURIComponent(c console.log("listening on : " + localHost ); console.log("connect securely to : " + config.origin ); console.log("serving files under : " + config.mountdir ); -if (config.run) console.log("running madoko using: " + config.run ); +if (config.run) console.log("running madoko using: " + config.run + " (concurrency: " + config.concurrency.toString() + ")"); console.log(""); console.log("---------------------------------------------------------------"); console.log("access server at : " + accessPoint ); diff --git a/support/madoko-local/src/run.js b/support/madoko-local/src/run.js index ad6f49b4..5dc665d3 100644 --- a/support/madoko-local/src/run.js +++ b/support/madoko-local/src/run.js @@ -165,7 +165,8 @@ function madokoRunIn( config, userpath, docname, files, target ) { return saveFiles( userpath, files ).then( function() { Sandbox.getSafePath(userpath,docname); // is docname safe? var tgtflag = (target===Target.Pdf ? " --pdf" : (target===Target.TexZip ? " --texzip" : "")); - var flags = "-vv --verbose-max=0 -mmath-embed:512 -mmath-concurrency:1 -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; + var flags = "-vv --verbose-max=0 -mmath-embed:512 -mmath-concurrency:" + config.concurrency.toString() + + " -membed:" + (target > Target.Math ? "512" : "0") + tgtflag; var extraflags = config.runflags || ""; var timeout = (target>Target.Math ? config.limits.timeoutPDF : config.limits.timeoutMath); var startTime = Date.now(); diff --git a/support/madoko-local/versionlog.json b/support/madoko-local/versionlog.json index 89e8ac0e..f36d0d0b 100644 --- a/support/madoko-local/versionlog.json +++ b/support/madoko-local/versionlog.json @@ -1,9 +1,10 @@ { "log": [ - { "version": "0.9.3", + { "version": "0.9.4", "date" : "2020-02-14", "updates": [ - "More liberal timeouts" + "More liberal timeouts", + "Add concurrency option" ] }, { "version": "0.9.1", diff --git a/versionlog.json b/versionlog.json index 091e749c..08c1f4c7 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,6 +1,6 @@ { "log" : [ - { "version": "1.1.11", + { "version": "1.1.12", "date": "2020-02-14", "updates": [ "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")", From 8d9806f18386480cb87fe19178e851b3292cd67a Mon Sep 17 00:00:00 2001 From: daan Date: Sat, 15 Feb 2020 05:48:51 -0800 Subject: [PATCH 45/46] add svg-bbox-exact option to speed up svg genaration by default. --- src/mathStaticRun.kk | 20 +++++++++++--------- src/options.kk | 1 + src/optionsMath.kk | 9 +++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/mathStaticRun.kk b/src/mathStaticRun.kk index 78bd90cf..5dc7f4b6 100644 --- a/src/mathStaticRun.kk +++ b/src/mathStaticRun.kk @@ -234,16 +234,17 @@ function groupPages( gcount : int, count : int, cur : pages, acc : list, } } -function dvisvgPages( pages : pages, concurrency : int ) : list { +function splitPages(pages : pages, concurrency : int) : list { val n = pages.count val g = if (n < 10 || concurrency <= 0) then 0 else ((n / concurrency) + 1); - val pss = if (g == 0) then [pages] else unsafeNoDiv{ groupPages(g,0,[],[],pages) } - pss.map( fun(ps) { - "-p" + ps.map( fun(rng) { + if (g <= 0) then [pages] else unsafeNoDiv{ groupPages(g,0,[],[],pages) } +} + +function dvisvgPages( pages : pages ) : string { + "-p" + pages.map( fun(rng) { val (lo,hi) = rng lo.show + (if (lo>=hi) then "" else "-" + hi.show) }).join(",") - }) } function convertPages( pages : pages ) : string { @@ -294,7 +295,7 @@ function mathConvImgCmd( mode : mathkind, fname : string, mrender : mathrender, // extension should be dvi or xdv // add 0.2pt (~0.07mm of extra bounding box for better rendering of svg's) val fuzz = 0.2 - val cmd = dvisvgPages(pages,opts.concurrency).map( fun(pageRange) { + val cmd = splitPages(pages,opts.concurrency).map(fun(xpages) { opts.dvisvg.quote + (if (opts.svgFontFormat=="" || opts.svgFontFormat=="none") then " -n" @@ -302,11 +303,12 @@ function mathConvImgCmd( mode : mathkind, fname : string, mrender : mathrender, then "" else " --font-format=" + opts.svgFontFormat.quote) + (if (fuzz > 0.0) then " -b" + fuzz.showFixed(1) + "pt" else "") + - " -e -j -v3 -d" + opts.svgPrec.show + " " + - pageRange + + (if (opts.svgBBoxExact) then " -e" else "") + + " -a -j -v3 -d" + opts.svgPrec.show + " " + + dvisvgPages(xpages) + " -o " + (baseImg + "-%1p.svg").quote + " " + fname.basename.quote - }).join(";") // run in parallel + }).join(";") (cmd,0,fuzz) } Png | fname.extname==".dvi" -> { diff --git a/src/options.kk b/src/options.kk index e7fff113..ba37f551 100644 --- a/src/options.kk +++ b/src/options.kk @@ -56,6 +56,7 @@ public struct mathoptions ( svgPrec : int = 3, // decimal points used in svg rendering svgDefs : string = "", // used internally to share svg definitions svgFontFormat: string = "", // embed fonts directly in svg, use 'none' or '' to use SVG paths + svgBBoxExact : bool = False, // use exact bounding box concurrency : int = 4, // max concurrency for generating math renderings diff --git a/src/optionsMath.kk b/src/optionsMath.kk index 585e2b19..e805e6d2 100644 --- a/src/optionsMath.kk +++ b/src/optionsMath.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -16,7 +16,7 @@ import options import optionsSandbox -public function updateMath( options:options, key: string, lvalue: string, value:string, ivalue: int, bvalue:bool ) : options +public function updateMath( options:options, key: string, lvalue: string, value:string, ivalue: int, bvalue:bool ) : options { if (key=="mathjax-ext" || key=="mathjax-extension") options(math = (options.math)(mjext=if (value=="") then "" else options.math.mjext + ";" + value)) elif (key=="math-mode") options.setMathMode(value) @@ -34,12 +34,13 @@ public function updateMath( options:options, key: string, lvalue: string, value: elif (key=="math-svg-precision") options(math = (options.math)(svgPrec=min(6,max(ivalue,0)))) elif (key=="math-svg-use-fonts") options(math = (options.math)(svgFontFormat=if (bvalue) then "woff,ah" else "none")) elif (key=="math-svg-font-format") options(math = (options.math)(svgFontFormat=lvalue)) + elif (key=="math-svg-bbox-exact") options(math = (options.math)(svgBBoxExact=bvalue)) elif (key=="mathjax") then (if (value!="") then options(math = (options.math)(mode=Dynamic, mathjax=(if (lvalue=="true") then "default" else value))) else options(math = (options.math)(mode=Static, mathjax=""))) else { options.updateSandbox(key,lvalue,value,ivalue) - } + } } @@ -61,4 +62,4 @@ function parseMathRender( options : options, value : string, def : maybe Date: Sat, 15 Feb 2020 06:40:12 -0800 Subject: [PATCH 46/46] bump version to 1.2.0 --- package.json | 2 +- src/block.kk | 5 +-- src/mathStatic.kk | 58 +++++++++++++++++------------------ versionlog.json | 9 +++--- web/client/scripts/ui.js | 8 +++-- web/package.json | 66 +++++++++++++++++++++------------------- 6 files changed, 77 insertions(+), 71 deletions(-) diff --git a/package.json b/package.json index 74f93580..06a73341 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "madoko", "author": "Daan Leijen, Microsoft Corp.", - "version": "1.1.12", + "version": "1.2.0", "homepage": "http://madoko.codeplex.com", "description": "Madoko is a fast scholarly Markdown processor written in Koka", "licenses": [ diff --git a/src/block.kk b/src/block.kk index 0c60d64d..6c0f5a95 100644 --- a/src/block.kk +++ b/src/block.kk @@ -446,8 +446,9 @@ function columns( row : string, context : blockContext, ofs: int ) : row { } val rxCellCodeInline = @"(?:``(?:[^`]|`(?!`))*``|`(?:[^`]|``)*`)" -val rxCellTexInline = @"(?:\$\{((?:[^\\\$]|\\[\s\S])+)\$)" -val rxCellContent = @"(?:\\.|"+ rxCellTexInline + @"|" + rxCellCodeInline + @"|[^\\|+]|\+ *(?![-=]))" +val rxCellTexInline1 = @"(?:\$(?!\{)(?:(?:[^\\\$\|]|\\[\s\S])+)\$)" +val rxCellTexInline2 = @"(?:\$\{(?:(?:[^\\\$]|\\[\s\S])+)\$)" +val rxCellContent = @"(?:\\.|"+ rxCellTexInline1 + @"|" + rxCellTexInline2 + @"|" + rxCellCodeInline + @"|[^\\|+]|\+ *(?![-=]))" val rxCell = regex(@"(?:^ *(?:\||\+(?=[-=])))?(" + rxCellContent + @"+(?:[|]+|[+]+(?= *[-=])|$))") val rxRowAttr = regex(@"[\+\|]" + xattrs + "$") diff --git a/src/mathStatic.kk b/src/mathStatic.kk index a52c0853..683dd327 100644 --- a/src/mathStatic.kk +++ b/src/mathStatic.kk @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------- Copyright 2013-2015 Microsoft Corporation. - + This is free software; you can redistribute it and/or modify it under the terms of the Apache License, Version 2.0. A copy of the License can be found in the file "license.txt" at the root of this distribution. @@ -37,33 +37,34 @@ function concurrent( tasks : list<(cont:(int) -> io ()) -> io ()>, continue : (i } } -function mathHeader( xopts : options, mode : mathkind ) : string +function mathHeader( xopts : options, mode : mathkind ) : string { val mrender = xopts.math.getMathRender(mode) val deps = ["% MathMode: " + mode.show, "MathRender: " + mrender.show, - "MathDpi: " + xopts.math.dpi.show, // for convience to easily rebuild math we include it always + "MathDpi: " + xopts.math.dpi.show, // for convience to easily rebuild math we include it always "MathEmbedLimit: " + xopts.math.embedLimit.show, "MathScale: " + xopts.math.getMathScale(mrender).show, "MathBaseline: " + xopts.math.baseline.show, "MathDocClass: " + xopts.math.docClass, "MathImgDir: " + xopts.math.imgDir, - "MathLatex: " + (if (mode.isPlain) then xopts.getMathLatex else xopts.getMathLatexFull), + "MathLatex: " + (if (mode.isPlain) then xopts.getMathLatex else xopts.getMathLatexFull), ] + (match(mrender) { - Svg -> [ + Svg -> [ "MathSvgFontFormat: " + xopts.math.svgFontFormat.show, "MathSvgSharePaths: " + xopts.math.svgShare.show, "MathSvgPrecision: " + xopts.math.svgPrec.show, - "Dvisvg: " + xopts.math.dvisvg + "MathSvgBBoxExact: " + xopts.math.svgBBoxExact.show, + "Dvisvg: " + xopts.math.dvisvg ] Png -> [ "Dvipng: " + xopts.math.dvipng, "Convert: " + xopts.math.convert, "Dvips: " + xopts.math.dvips, - "Ps2pdf: " + xopts.math.ps2pdf, + "Ps2pdf: " + xopts.math.ps2pdf, ] - }) + }) deps.join(", ") + "\n" } @@ -77,11 +78,11 @@ function mathContent( xopts : options, mode : mathkind, body : string = "" ) : s function mathFormat( xopts : options, mode :mathkind, math : string, outName : string ) : (string,string,string) { val texName = outName.appendStem("-math-" + mode.show).changeExt(".tex") - val texFinalName = texName.changeExt(".final.tex") + val texFinalName = texName.changeExt(".final.tex") (texName,texFinalName,mathContent(xopts,mode,math)) } -public alias runMathStaticFun +public alias runMathStaticFun = ( content : string, inName : string, outName : string, texNamePlain : string, texNameFull : string, plainPages : pages, fullPages : pages, @@ -89,10 +90,10 @@ public alias runMathStaticFun opts : options, continue : (maybe<(dict,string)>) -> io () ) -> io () -public function mathStatic( mathPlain : string, mathFull : string, - inName : string, outName : string, xopts : options, content : string, - runMathStatic : runMathStaticFun, - continue : (maybe<(dict,string)>) -> io () ) : io () +public function mathStatic( mathPlain : string, mathFull : string, + inName : string, outName : string, xopts : options, content : string, + runMathStatic : runMathStaticFun, + continue : (maybe<(dict,string)>) -> io () ) : io () { val (texNamePlain,texNameFinalPlain,outMathPlain) = xopts.mathFormat(Plain,mathPlain,outName) val (texNameFull,texNameFinalFull,outMathFull) = xopts.mathFormat(Full,mathFull,outName) @@ -108,11 +109,11 @@ public function mathStatic( mathPlain : string, mathFull : string, } */ val dimxName = inName.changeExt(".dimx") - if (fexistsSync(dimxName) && outMathPlain == oldMathPlain && outMathFull == oldMathFull && !(xopts.rebuild)) + if (fexistsSync(dimxName) && outMathPlain == oldMathPlain && outMathFull == oldMathFull && !(xopts.rebuild)) then return continue(Nothing) xopts.print("rendering math.") - if ((outMathPlain != oldMathPlain || xopts.rebuild || !fexistsSync(texNamePlain)) && + if ((outMathPlain != oldMathPlain || xopts.rebuild || !fexistsSync(texNamePlain)) && !(tryWriteTextFile(texNamePlain,outMathPlain))) { xopts.printErr("error: unable to write: " + texNamePlain) return continue(Nothing) @@ -121,12 +122,12 @@ public function mathStatic( mathPlain : string, mathFull : string, xopts.printErr("error: unable to write: " + texNameFull) return continue(Nothing) } - + val rebuildPlain = oldMathPlain.unsnippet("old plain") != outMathPlain.unsnippet("new plain") val rebuildFull = oldMathFull.unsnippet("old full") != outMathFull.unsnippet("new full") // if (rebuildPlain || rebuildFull) trace("math rebuild") - val plainPages = mathAnalyseChanged(outMathPlain,xopts.math.dim,xopts.rebuild || rebuildPlain) - val fullPages = mathAnalyseChanged(outMathFull,xopts.math.dim,xopts.rebuild || rebuildFull) + val plainPages = mathAnalyseChanged(outMathPlain,xopts.math.dim,xopts.rebuild || rebuildPlain) + val fullPages = mathAnalyseChanged(outMathFull,xopts.math.dim,xopts.rebuild || rebuildFull) if (xopts.verbose>=1) { if (plainPages.isNil && mathPlain.containsSnippet) xopts.print("math unchanged (plain)") @@ -134,11 +135,11 @@ public function mathStatic( mathPlain : string, mathFull : string, } if (plainPages.isNil && fullPages.isNil) then { tryRename(texNamePlain,texNameFinalPlain); // save result to prevent rebuilds - tryRename(texNameFull,texNameFinalFull); + tryRename(texNameFull,texNameFinalFull); return continue(Nothing) } - - runMathStatic( content, inName, outName, texNamePlain, texNameFull, + + runMathStatic( content, inName, outName, texNamePlain, texNameFull, plainPages, fullPages, oldMathPlain, oldMathFull, xopts, continue ) } @@ -162,13 +163,13 @@ public function count( ps : pages ) : int { } function range(lo,n) { if (lo<=0) then [] else [(lo,lo+n)] } - + function compress( pages : list, lo : int = ~1, n : int = 0 ) : pages { match(pages) { Nil -> range(lo,n) Cons(p,ps) -> { - if (p==lo+n+1) - then compress(ps,lo,n+1) + if (p==lo+n+1) + then compress(ps,lo,n+1) else range(lo,n) + compress(ps,p,0) } } @@ -179,15 +180,15 @@ public function inside( page : int, pages : pages ) : bool { Nil -> False Cons((lo,hi),ps) -> { (lo <= page && page <= hi) || inside(page,ps) - } + } } } function mathAnalyseChanged( snippets : string, dim : dict, rebuild : bool ) : pages -{ +{ val digests = snippets.findAll(rxDigest).list.mapIndexed( fun(idx,cap) { (idx+1,cap.groups[1]) }) val maxPage = digests.length - + val pages = digests.concat fun(idigest) { val (current,digest) = idigest match(dim[digest]) { @@ -197,4 +198,3 @@ function mathAnalyseChanged( snippets : string, dim : dict, rebuild : } pages.compress } - diff --git a/versionlog.json b/versionlog.json index 08c1f4c7..c0168c63 100644 --- a/versionlog.json +++ b/versionlog.json @@ -1,10 +1,11 @@ { "log" : [ - { "version": "1.1.12", - "date": "2020-02-14", + { "version": "1.2.0", + "date": "2020-02-15", "updates": [ - "Run dvisvgm in parallel (default: \"Math Concurrency: 4\")", - "Allow 2 minute process timeout in sandbox mode", + "Run dvisvgm in parallel", + "Use non-exact bounding box for svg math by default (\"Math Svg Bbox Exact: False\")", + "Allow longer process timeout in sandbox mode" ] }, { "version": "1.1.8", diff --git a/web/client/scripts/ui.js b/web/client/scripts/ui.js index dc535ab0..21e56b24 100644 --- a/web/client/scripts/ui.js +++ b/web/client/scripts/ui.js @@ -1681,7 +1681,8 @@ var UI = (function() { function(ctx) { // self.asyncServer.clearStale(); // stale is usually set by intermediate madoko runs // run madoko locally again using our generated files (and force a run) - return self.asyncMadoko.run(true); + self.asyncMadoko.run(true); + return "Rendering math done"; }, function(err) { self.onError(err); @@ -2475,8 +2476,9 @@ var UI = (function() { -------------------------------------------------- */ function reformatTable( lines, column ) { var rxCellCodeInline = /(?:``(?:[^`]|`(?!`))*``|`(?:[^`]|``)*`)/.source; - var rxCellTexInline = /(?:\$\{(?:[^\\\$]|\\[\s\S])+\$)/.source; - var rxCellContent = /\\./.source + "|" + rxCellTexInline + "|" + rxCellCodeInline + "|" + /[^\\|+]|\+ *(?!$|[:~=\-\r\n])/.source; + var rxCellTexInline1 = /(?:\$(?!\{)(?:[^\\\$\|]|\\[\s\S])+\$)/.source; + var rxCellTexInline2 = /(?:\$\{(?:[^\\\$]|\\[\s\S])+\$)/.source; + var rxCellContent = /\\./.source + "|" + rxCellTexInline1 + "|" + rxCellTexInline2 + "|" + rxCellCodeInline + "|" + /[^\\|+]|\+ *(?!$|[:~=\-\r\n])/.source; var rxCellContents = "((?:" + rxCellContent + ")+)"; var rxCell = new RegExp(/((?:^ *(?:\||\+(?=[:=~-])))?)/.source + rxCellContents + /([|]+|[\+]+(?= *[:~=\-\r\n]| *$))/.source, "g"); //var rxCell = /((?:^ *(?:\||\+(?=[:=~-])))?)((?:[^\\|+]|\\.|\+ *(?!$|[:~=\-\r\n]))+)([|]+|[\+]+(?= *[:~=\-\r\n]| *$))/g; diff --git a/web/package.json b/web/package.json index 0465ab9d..0774a62a 100644 --- a/web/package.json +++ b/web/package.json @@ -1,44 +1,46 @@ { - "name" : "madoko.net", - "author" : "Daan Leijen, Microsoft Corp.", - "version" : "1.1.6", - "homepage" : "http://madoko.codeplex.com", - "description" : "Madoko is a fast scholarly Markdown processor written in Koka", - "licenses": [{ - "type" : "Apache License 2.0", - "url" : "http://www.apache.org/licenses/LICENSE-2.0.html" - }], + "name": "madoko.net", + "author": "Daan Leijen, Microsoft Corp.", + "version": "1.2.0", + "homepage": "http://madoko.codeplex.com", + "description": "Madoko is a fast scholarly Markdown processor written in Koka", + "licenses": [ + { + "type": "Apache License 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + } + ], "keywords": [ - "Madoko", - "Markdown", - "LaTeX", - "Koka", - "Microsoft", - "Scholarly", - "Academic" + "Madoko", + "Markdown", + "LaTeX", + "Koka", + "Microsoft", + "Scholarly", + "Academic" ], - "engines" : { - "node": ">=0.10.0" + "engines": { + "node": ">=0.10.0" }, "dependencies": { - "amdefine" : ">=0.0.4", - "requirejs" : ">=2.1", - "request" : ">=2", - "mkdirp" : ">=0.3.5" + "amdefine": ">=0.0.4", + "requirejs": ">=2.1", + "request": ">=2", + "mkdirp": "^0.3.5" }, "devDependencies": { - "jake" : ">=0.7.6", - "express" : ">=4.1.1", - "body-parser" : ">=1.0.2", - "cookie-session" : "=1.0.2", - "rimraf" : ">=2.2.8", + "jake": ">=0.7.6", + "express": ">=4.1.1", + "body-parser": ">=1.0.2", + "cookie-session": "=1.0.2", + "rimraf": ">=2.2.8", "recursive-readdir": ">=1.2.0", - "minimatch" : ">=2.0.1", - "compression" : ">=1.6.0", - "commander" : ">=2.8.1" + "minimatch": ">=2.0.1", + "compression": ">=1.6.0", + "commander": ">=2.8.1" }, "repository": { - "type": "hg", - "url" : "https://hg.codeplex.com/madoko" + "type": "hg", + "url": "https://hg.codeplex.com/madoko" } }