Skip to content

Commit

Permalink
Adopt registerNotebookSerializer
Browse files Browse the repository at this point in the history
  • Loading branch information
Jackson Kearl committed Apr 27, 2021
1 parent f0f3525 commit 9c0f86c
Show file tree
Hide file tree
Showing 8 changed files with 2,219 additions and 1,258 deletions.
1 change: 0 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/class-name-casing": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
Expand Down
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,9 @@
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off"
"typescript.tsc.autoDetect": "off",

"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"icon": "notebook.png",
"engines": {
"vscode": "^1.50.0"
"vscode": "^1.56.0"
},
"categories": [
"Notebooks"
Expand Down Expand Up @@ -46,12 +46,12 @@
]
],
"typescript": [
"ts-node",
"node",
[
"-T",
"--skip-project",
"-e",
"${code}"
"(async () => { ${code} } )()"
]
],
"javascript": [
Expand Down Expand Up @@ -150,9 +150,9 @@
"@types/mocha": "^7.0.2",
"@types/node": "^13.11.0",
"@types/user-home": "^2.0.0",
"@types/vscode": "^1.50.0",
"@typescript-eslint/eslint-plugin": "^2.30.0",
"@typescript-eslint/parser": "^2.30.0",
"@types/vscode": "1.55.0",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"eslint": "^6.8.0",
"glob": "^7.1.6",
"mocha": "^7.1.2",
Expand All @@ -162,4 +162,4 @@
"dependencies": {
"user-home": "^2.0.0"
}
}
}
132 changes: 32 additions & 100 deletions src/MarkdownProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,115 +5,46 @@

import * as vscode from 'vscode';

export const providerOptions = {
transientMetadata: {
runnable: true,
editable: true,
custom: true,
},
transientOutputs: true
export const providerOptions: vscode.NotebookDocumentContentOptions = {
transientOutputs: true,
};

const getMostCommonFileType = async () => {
const types = [
['js', 'javascript'],
['ts', 'typescript'],
['py', 'python'],
['rb', 'ruby'],
['sh', 'bash'],
];
const max = { name: 'bash', number: 0 };
await Promise.all(types.map(async ([ext, name]) => {
const results = await vscode.workspace.findFiles('src/*.' + ext, undefined, 100);
if (results.length > max.number) { max.number = results.length; max.name = name; }
}));
return max.name;
};

export class MarkdownProvider implements vscode.NotebookContentProvider {
options?: vscode.NotebookDocumentContentOptions = providerOptions;

onDidChangeNotebookContentOptions?: vscode.Event<vscode.NotebookDocumentContentOptions> | undefined;

async resolveNotebook(document: vscode.NotebookDocument, webview: vscode.NotebookCommunication): Promise<void> { }

async backupNotebook(document: vscode.NotebookDocument, context: vscode.NotebookDocumentBackupContext, cancellation: vscode.CancellationToken): Promise<vscode.NotebookDocumentBackup> {
await this.saveNotebookAs(context.destination, document, cancellation);
return {
id: context.destination.toString(),
delete: () => vscode.workspace.fs.delete(context.destination)
};
}
export class MarkdownProvider implements vscode.NotebookSerializer {
private lastSelections: { code: string; lang: string; }[] = [];

private lastSelection: { code: string, lang: string } | undefined;
setLastSelection(selection: { code: string, lang: string }) {
this.lastSelection = selection;
setLastSelection(selection: { code: string; lang: string; }) {
this.lastSelections.push(selection);
}

async openNotebook(uri: vscode.Uri, openContext: vscode.NotebookDocumentOpenContext): Promise<vscode.NotebookData> {
const lastSelection = this.lastSelection;
this.lastSelection = undefined;

if (openContext.backupId) {
uri = vscode.Uri.parse(openContext.backupId);
deserializeNotebook(content: Uint8Array, token: vscode.CancellationToken): vscode.NotebookData | Thenable<vscode.NotebookData> {
if (content.length === 0) {
const cells = this.lastSelections.map((cell) => new vscode.NotebookCellData(vscode.NotebookCellKind.Markdown, cell.code, cell.lang));
this.lastSelections = [];
return new vscode.NotebookData(cells);
}
const languages = Object.keys(vscode.workspace.getConfiguration('handydandy-notebook').get('dispatch') as Record<string, [string, string[]]>);
const metadata: vscode.NotebookDocumentMetadata = { editable: true, cellEditable: true, cellHasExecutionOrder: false, cellRunnable: true, runnable: true };

const content = uri.scheme === 'untitled'
? lastSelection ? `\`\`\`${lastSelection.lang}\n${lastSelection.code}\n\`\`\`` : ''
: Buffer.from(await vscode.workspace.fs.readFile(uri)).toString('utf8');

let cellRawData: RawNotebookCell[];
if (content.trim().length === 0) {
const type = await getMostCommonFileType();
cellRawData = parseMarkdown('```' + type + '\n```');
} else {
cellRawData = parseMarkdown(content.trim().length ? content : '```' + '' + '\n```');
}
const cells = cellRawData.map(rawToNotebookCellData);

return {
languages,
metadata,
cells
};
}

async saveNotebook(document: vscode.NotebookDocument, cancellation: vscode.CancellationToken): Promise<void> {
const stringOutput = writeCellsToMarkdown(document.cells);
await vscode.workspace.fs.writeFile(document.uri, Buffer.from(stringOutput));
const str = Buffer.from(content).toString();
const cells = parseMarkdown(str);
return new vscode.NotebookData(cells.map((cell) => new vscode.NotebookCellData(cell.kind, cell.content, cell.language)));
}

async saveNotebookAs(targetResource: vscode.Uri, document: vscode.NotebookDocument, cancellation: vscode.CancellationToken): Promise<void> {
const stringOutput = writeCellsToMarkdown(document.cells);
await vscode.workspace.fs.writeFile(targetResource, Buffer.from(stringOutput));
serializeNotebook(data: vscode.NotebookData, token: vscode.CancellationToken): Uint8Array | Thenable<Uint8Array> {
const md = writeCellsToMarkdown(data.cells);
return Buffer.from(md);
}

private _onDidChangeNotebook = new vscode.EventEmitter<vscode.NotebookDocumentEditEvent>();
readonly onDidChangeNotebook = this._onDidChangeNotebook.event;
}

export function rawToNotebookCellData(data: RawNotebookCell): vscode.NotebookCellData {
return <vscode.NotebookCellData>{
cellKind: data.kind,
language: data.language,
metadata: { editable: true, runnable: true, custom: { leadingWhitespace: data.leadingWhitespace, trailingWhitespace: data.trailingWhitespace, indentation: data.indentation } },
outputs: [],
source: data.content
};
}


export interface RawNotebookCell {
indentation?: string;
leadingWhitespace: string;
trailingWhitespace: string;
language: string;
content: string;
kind: vscode.CellKind;
kind: vscode.NotebookCellKind;
}


const LANG_IDS = new Map([
['bat', 'batch'],
['c++', 'cpp'],
Expand All @@ -124,6 +55,7 @@ const LANG_IDS = new Map([
['py2', 'python'],
['py3', 'python'],
]);

const LANG_ABBREVS = new Map(
Array.from(LANG_IDS.keys()).map(k => [LANG_IDS.get(k), k])
);
Expand Down Expand Up @@ -208,7 +140,7 @@ export function parseMarkdown(content: string): RawNotebookCell[] {
cells.push({
language,
content,
kind: vscode.CellKind.Code,
kind: vscode.NotebookCellKind.Code,
leadingWhitespace: leadingWhitespace,
trailingWhitespace: trailingWhitespace,
indentation: codeBlockStart.indentation
Expand All @@ -235,7 +167,7 @@ export function parseMarkdown(content: string): RawNotebookCell[] {
cells.push({
language: 'markdown',
content,
kind: vscode.CellKind.Markdown,
kind: vscode.NotebookCellKind.Markdown,
leadingWhitespace: leadingWhitespace,
trailingWhitespace: trailingWhitespace
});
Expand All @@ -244,43 +176,43 @@ export function parseMarkdown(content: string): RawNotebookCell[] {
return cells;
}

export function writeCellsToMarkdown(cells: ReadonlyArray<vscode.NotebookCell>): string {
export function writeCellsToMarkdown(cells: ReadonlyArray<vscode.NotebookCellData>): string {
let result = '';
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
if (i === 0) {
result += cell.metadata.custom?.leadingWhitespace ?? '';
result += cell.metadata?.custom?.leadingWhitespace ?? '';
}

if (cell.cellKind === vscode.CellKind.Code) {
const indentation = cell.metadata.custom?.indentation || '';
if (cell.kind === vscode.NotebookCellKind.Code) {
const indentation = cell.metadata?.custom?.indentation || '';
const languageAbbrev = LANG_ABBREVS.get(cell.language) || cell.language;
const codePrefix = indentation + '```' + languageAbbrev + '\n';
const contents = cell.document.getText().split(/\r?\n/g)
const contents = cell.source.split(/\r?\n/g)
.map(line => indentation + line)
.join('\n');
const codeSuffix = '\n' + indentation + '```';

result += codePrefix + contents + codeSuffix;
} else {
result += cell.document.getText();
result += cell.source;
}

result += getBetweenCellsWhitespace(cells, i);
}
return result;
}

function getBetweenCellsWhitespace(cells: ReadonlyArray<vscode.NotebookCell>, idx: number): string {
function getBetweenCellsWhitespace(cells: ReadonlyArray<vscode.NotebookCellData>, idx: number): string {
const thisCell = cells[idx];
const nextCell = cells[idx + 1];

if (!nextCell) {
return thisCell.metadata.custom?.trailingWhitespace ?? '\n';
return thisCell.metadata?.custom?.trailingWhitespace ?? '\n';
}

const trailing = thisCell.metadata.custom?.trailingWhitespace;
const leading = nextCell.metadata.custom?.leadingWhitespace;
const trailing = thisCell.metadata?.custom?.trailingWhitespace;
const leading = nextCell.metadata?.custom?.leadingWhitespace;

if (typeof trailing === 'string' && typeof leading === 'string') {
return trailing + leading;
Expand Down
Loading

0 comments on commit 9c0f86c

Please sign in to comment.