Skip to content

Commit

Permalink
Merge pull request #278 from marp-team/pdf-note-annotations
Browse files Browse the repository at this point in the history
Add `markdown.marp.pdf.noteAnnotations` config
  • Loading branch information
yhatt authored Aug 21, 2021
2 parents 9a43048 + 4cb5bac commit 17dd74b
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Auto completion for [`size` global directive](https://github.com/marp-team/marp-core#size-global-directive) ([#276](https://github.com/marp-team/marp-vscode/pull/276))
- `unknown-size` diagnostic: Notify if the specified size preset was not defined in a theme ([#276](https://github.com/marp-team/marp-vscode/pull/276))
- Auto-trigger suggestion for value of supported directives ([#277](https://github.com/marp-team/marp-vscode/pull/277))
- `markdown.marp.pdf.noteAnnotations` config: Add presenter notes to exported PDF as note annotations ([#278](https://github.com/marp-team/marp-vscode/pull/278))

### Changed

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@
"default": true,
"description": "Enables the outline extension for Marp Markdown. If enabled, VS Code's outline view will reflect slide splitters, and you can fold regions of the slide content in the editor."
},
"markdown.marp.pdf.noteAnnotations": {
"type": "boolean",
"default": false,
"markdownDescription": "Adds [presenter notes](https://marpit.marp.app/usage?id=presenter-notes) to exported PDF as note annotations."
},
"markdown.marp.themes": {
"type": "array",
"default": [],
Expand Down
1 change: 1 addition & 0 deletions src/__mocks__/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const defaultConf: MockedConf = {
'markdown.marp.enableHtml': false,
'markdown.marp.exportType': 'pdf',
'markdown.marp.outlineExtension': true,
'markdown.marp.pdf.noteAnnotations': false,
'window.zoomLevel': 0,
}

Expand Down
77 changes: 50 additions & 27 deletions src/commands/export.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { commands, env, window, workspace } from 'vscode'
import * as marpCli from '../marp-cli'
import * as option from '../option'
import { createWorkspaceProxyServer } from '../workspace-proxy-server'
import * as exportModule from './export'

Expand Down Expand Up @@ -111,7 +112,7 @@ describe('#saveDialog', () => {

expect(window.showSaveDialog).toHaveBeenCalledWith(
expect.objectContaining({
defaultUri: expect.objectContaining({ fsPath: '/tmp/test' }),
defaultUri: expect.objectContaining({ fsPath: '/tmp/test.pdf' }),
})
)
})
Expand All @@ -122,7 +123,12 @@ describe('#saveDialog', () => {
await exportModule.saveDialog(document)
expect(window.showSaveDialog).toHaveBeenCalled()

const { filters } = (window.showSaveDialog as jest.Mock).mock.calls[0][0]
const { defaultUri, filters } = (window.showSaveDialog as jest.Mock).mock
.calls[0][0]

expect(defaultUri).toStrictEqual(
expect.objectContaining({ fsPath: '/tmp/test.pptx' })
)
expect(Object.values(filters)[0]).toStrictEqual(['pptx'])
})

Expand All @@ -149,43 +155,66 @@ describe('#saveDialog', () => {
})

describe('#doExport', () => {
const saveURI: any = {
scheme: 'file',
path: '/tmp/to.pdf',
fsPath: '/tmp/to.pdf',
const saveURI = (scheme = 'file', ext = 'pdf'): any => {
const instance = {
scheme,
path: `/tmp/to.${ext}`,
fsPath: `/tmp/to.${ext}`,
toString: () => `${scheme}://${instance.path}`,
}
return instance
}

const document: any = {
uri: { scheme: 'file', path: '/tmp/md.md', fsPath: '/tmp/md.md' },
}

it('exports passed document via Marp CLI and opens it', async () => {
const runMarpCLI = jest.spyOn(marpCli, 'default').mockImplementation()
const uri = saveURI()

await exportModule.doExport(saveURI, document)
await exportModule.doExport(uri, document)
expect(runMarpCLI).toHaveBeenCalled()
expect(env.openExternal).toHaveBeenCalledWith(saveURI)
expect(env.openExternal).toHaveBeenCalledWith(uri)
})

it('shows warning when Marp CLI throws error', async () => {
jest.spyOn(marpCli, 'default').mockRejectedValue(new Error('ERROR'))

await exportModule.doExport(saveURI, document)
await exportModule.doExport(saveURI(), document)
expect(window.showErrorMessage).toHaveBeenCalledWith(
expect.stringContaining('[Error] ERROR')
)
})

describe('when the save path has non-file scheme', () => {
const saveURI = (scheme: string, ext: string): any => {
const instance = {
scheme,
path: `/tmp/to.${ext}`,
fsPath: `/tmp/to.${ext}`,
toString: () => `${scheme}://${instance.path}`,
}
return instance
}
describe('when enabled markdown.marp.pdf.noteAnnotations', () => {
beforeEach(() => {
setConfiguration({ 'markdown.marp.pdf.noteAnnotations': true })
jest.spyOn(marpCli, 'default').mockImplementation()
})

it('enables pdfNotes option while exporting PDF', async () => {
const optionGeneratorSpy = jest.spyOn(option, 'marpCoreOptionForCLI')
await exportModule.doExport(saveURI('file', 'pdf'), document)

expect(optionGeneratorSpy).toHaveBeenCalledWith(
document,
expect.objectContaining({ pdfNotes: true })
)
})

it('disables pdfNotes option while exporting to other extensions', async () => {
const optionGeneratorSpy = jest.spyOn(option, 'marpCoreOptionForCLI')
await exportModule.doExport(saveURI('file', 'pptx'), document)

expect(optionGeneratorSpy).toHaveBeenCalledWith(
document,
expect.objectContaining({ pdfNotes: false })
)
})
})

describe('when the save path has non-file scheme', () => {
it('exports the document into temporally path and copy it to the save path', async () => {
jest.spyOn(marpCli, 'default').mockImplementation()

Expand Down Expand Up @@ -253,7 +282,7 @@ describe('#doExport', () => {
.spyOn(workspace, 'getWorkspaceFolder')
.mockReturnValue(virtualWorkspace)

await exportModule.doExport(saveURI, vfsDocument)
await exportModule.doExport(saveURI(), vfsDocument)
expect(createWorkspaceProxyServer).toHaveBeenCalledWith(virtualWorkspace)

// dispose method
Expand All @@ -264,18 +293,12 @@ describe('#doExport', () => {
})

it('does not open workspace proxy server while exporting to html', async () => {
const saveURIHtml: any = {
scheme: 'file',
path: '/tmp/to.html',
fsPath: '/tmp/to.html',
}

jest.spyOn(marpCli, 'default').mockImplementation()
jest
.spyOn(workspace, 'getWorkspaceFolder')
.mockReturnValue(virtualWorkspace)

await exportModule.doExport(saveURIHtml, vfsDocument)
await exportModule.doExport(saveURI('file', 'html'), vfsDocument)
expect(createWorkspaceProxyServer).not.toHaveBeenCalled()
})
})
Expand Down
11 changes: 9 additions & 2 deletions src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ export const doExport = async (uri: Uri, document: TextDocument) => {

try {
let outputPath = uri.fsPath

const ouputExt = path.extname(uri.path)
const outputToLocalFS = uri.scheme === 'file'

// NOTE: It may return `undefined` if VS Code does not know about the
Expand All @@ -110,13 +112,16 @@ export const doExport = async (uri: Uri, document: TextDocument) => {
if (!outputToLocalFS) {
outputPath = path.join(
os.tmpdir(),
`marp-vscode-tmp-${nanoid()}${path.extname(uri.path)}`
`marp-vscode-tmp-${nanoid()}${ouputExt}`
)
}

// Run Marp CLI
const conf = await createConfigFile(document, {
allowLocalFiles: !proxyServer,
pdfNotes:
ouputExt === '.pdf' &&
marpConfiguration().get<boolean>('pdf.noteAnnotations'),
})

try {
Expand Down Expand Up @@ -168,16 +173,18 @@ export const saveDialog = async (document: TextDocument) => {

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const defaultType = marpConfiguration().get<string>('exportType')!
const ext = extensions[defaultType] ? `.${extensions[defaultType][0]}` : ''
const baseTypes = Object.keys(extensions)
const types = [...new Set<string>([defaultType, ...baseTypes])]

const saveURI = await window.showSaveDialog({
defaultUri: Uri.file(fsPath.slice(0, -path.extname(fsPath).length)),
defaultUri: Uri.file(fsPath.slice(0, -path.extname(fsPath).length) + ext),
filters: types.reduce((f, t) => {
if (baseTypes.includes(t)) f[descriptions[t]] = extensions[t]
return f
}, {}),
saveLabel: 'Export',
title: 'Export slide deck',
})

if (saveURI) {
Expand Down
10 changes: 9 additions & 1 deletion src/option.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,16 @@ describe('Option', () => {
it('returns basic options', async () => {
const opts = await subject({ uri: untitledUri })

// --allow-local-files
expect(opts.allowLocalFiles).toBe(true)
expect(opts.pdfNotes).toBeUndefined()

const custom = await subject(
{ uri: untitledUri },
{ allowLocalFiles: false, pdfNotes: true }
)

expect(custom.allowLocalFiles).toBe(false)
expect(custom.pdfNotes).toBe(true)
})

it('enables HTML by preference', async () => {
Expand Down
6 changes: 5 additions & 1 deletion src/option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,16 @@ export const marpCoreOptionForPreview = (

export const marpCoreOptionForCLI = async (
{ uri }: TextDocument,
{ allowLocalFiles = true }: { allowLocalFiles?: boolean } = {}
{
allowLocalFiles = true,
pdfNotes,
}: { allowLocalFiles?: boolean; pdfNotes?: boolean } = {}
) => {
const confMdPreview = workspace.getConfiguration('markdown.preview', uri)

const baseOpts = {
allowLocalFiles,
pdfNotes,
html: enableHtml() || undefined,
options: {
markdown: {
Expand Down

0 comments on commit 17dd74b

Please sign in to comment.