Skip to content

Commit

Permalink
Add Ancillary functionality to TOC Tree
Browse files Browse the repository at this point in the history
  • Loading branch information
sparksam committed Jun 25, 2024
1 parent 5e0dffd commit fdd527c
Show file tree
Hide file tree
Showing 13 changed files with 220 additions and 44 deletions.
13 changes: 11 additions & 2 deletions client/specs/book-tocs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@ const testTocPage: ClientTocNode = {
fileId: 'fileId'
}
}
const testTocAncillary: ClientTocNode = {
type: TocNodeKind.Ancillary,
value: {
absPath: '/path/to/ancillary',
token: 'token',
title: 'title',
fileId: 'fileId'
}
}
const testTocSubbook: ClientTocNode = {
type: TocNodeKind.Subbook,
value: { token: 'token', title: 'title' },
children: [testTocPage]
children: [testTocPage, testTocAncillary]
}
const testToc: BookToc = {
type: BookRootNode.Singleton,
Expand All @@ -27,13 +36,13 @@ const testToc: BookToc = {
licenseUrl: 'licenseUrl',
tocTree: [testTocSubbook]
}

describe('Toc Provider', () => {
const p = new TocsTreeProvider()
it('returns tree items for children', () => {
expect(p.getTreeItem(testToc)).toMatchSnapshot()
expect(p.getTreeItem(testTocSubbook)).toMatchSnapshot()
expect(p.getTreeItem(testTocPage)).toMatchSnapshot()
expect(p.getTreeItem(testTocAncillary)).toMatchSnapshot()
})
it('filters fileids when filtering is set', () => {
const p = new TocsTreeProvider()
Expand Down
2 changes: 1 addition & 1 deletion client/src/book-tocs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ export class TocsTreeProvider implements TreeDataProvider<BookOrTocNode> {
return [...this.bookTocs, ...this.orphans]
} else if (node.type === BookRootNode.Singleton) {
kids = node.tocTree
} else if (node.type === TocNodeKind.Page) {
} else if (node.type === TocNodeKind.Page || node.type === TocNodeKind.Ancillary) {
kids = []
} else {
kids = node.children
Expand Down
7 changes: 6 additions & 1 deletion client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { type BookOrTocNode, TocsTreeProvider } from './book-tocs'
import { type BooksAndOrphans, EMPTY_BOOKS_AND_ORPHANS, ExtensionServerNotification } from '../../common/src/requests'
import { readmeGenerator } from './generate-readme'
import { TocsEventHandler } from './tocs-event-handler'
import { TocNodeKind } from '../../common/src/toc'

let tocTreesView: vscode.TreeView<BookOrTocNode>
let tocTreesProvider: TocsTreeProvider
Expand Down Expand Up @@ -109,8 +110,12 @@ function doRest(client: LanguageClient): ExtensionExports {
vscode.commands.registerCommand('openstax.generateReadme', ensureCatch(readmeGenerator(hostContext)))
tocTreesView = vscode.window.createTreeView('tocTrees', { treeDataProvider: tocTreesProvider, showCollapseAll: true, dragAndDropController: tocEventHandler })
vscode.commands.registerCommand('openstax.toggleTocTreesFiltering', ensureCatch(toggleTocTreesFilteringHandler(tocTreesView, tocTreesProvider)))
vscode.commands.registerCommand('openstax.addAncillaryToToc', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.addNode(TocNodeKind.Ancillary, node, 'test') }))
vscode.commands.registerCommand('openstax.addPageToToc', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.addNode(TocNodeKind.Page, node, 'test') }))
vscode.commands.registerCommand('openstax.addSubBookToToc', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.addNode(TocNodeKind.Subbook, node, 'test') }))
vscode.commands.registerCommand('openstax.validateContent', ensureCatch(validateContent))
vscode.commands.registerCommand('openstax.test', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.removeNode(node) }))
vscode.commands.registerCommand('openstax.removeNode', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.removeNode(node) }))
vscode.commands.registerCommand('openstax.renameNode', ensureCatch(async (node: BookOrTocNode) => { await tocEventHandler.renameNode(node) }))
void ensureCatchPromise(setDefaultGitConfig())
void ensureCatchPromise(initPrivateSubmodule(hostContext))

Expand Down
6 changes: 3 additions & 3 deletions client/src/panel-toc-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export type PanelIncomingMessage = (
)

export type TreeItemWithToken = TreeItemUI & ({
type: TocNodeKind.Page
type: TocNodeKind.Page | TocNodeKind.Ancillary
token: string
title: string | undefined
fileId: string
Expand All @@ -51,7 +51,7 @@ export interface PanelState {
}

function toTreeItem(n: ClientTocNode): TreeItemWithToken {
if (n.type === TocNodeKind.Page) {
if (n.type === TocNodeKind.Page || n.type === TocNodeKind.Ancillary) {
return {
type: n.type,
token: n.value.token,
Expand Down Expand Up @@ -162,7 +162,7 @@ export class TocEditorPanel extends Panel<PanelIncomingMessage, never, PanelStat
protected getState(): PanelState {
const allModules = new Set<ClientPageish>()
function recAddModules(n: ClientTocNode) {
if (n.type === TocNodeKind.Page) {
if (n.type === TocNodeKind.Page || n.type === TocNodeKind.Ancillary) {
allModules.add(n.value)
} else {
n.children.forEach(recAddModules)
Expand Down
84 changes: 81 additions & 3 deletions client/src/tocs-event-handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import vscode from 'vscode'
import { type TocsTreeProvider, type BookOrTocNode } from './book-tocs'
import { type TocModification, TocModificationKind, type TocModificationParams, TocNodeKind, BookRootNode } from '../../common/src/toc'
import { type TocModification, TocModificationKind, type TocModificationParams, TocNodeKind, BookRootNode, type CreatePageEvent, type CreateSubbookEvent, type CreateAncillaryEvent } from '../../common/src/toc'
import { ExtensionServerRequest } from '../../common/src/requests'
import { expect, getRootPathUri } from './utils'
import { type ExtensionHostContext } from './panel'

const getNodeToken = (node: BookOrTocNode) => {
return node.type === TocNodeKind.Page || node.type === TocNodeKind.Subbook
return node.type === TocNodeKind.Page || node.type === TocNodeKind.Subbook || node.type === TocNodeKind.Ancillary
? node.value.token
: undefined
}
Expand All @@ -26,7 +26,7 @@ export class TocsEventHandler implements vscode.TreeDragAndDropController<BookOr
return expect(getRootPathUri(), 'Could not get root path uri').toString()
}

private async fireEvent(event: TocModification) {
private async fireEvent(event: TocModification | CreateSubbookEvent | CreatePageEvent | CreateAncillaryEvent) {
const workspaceUri = this.workspaceUri
const params: TocModificationParams = { workspaceUri, event }
await this.context.client.sendRequest(
Expand Down Expand Up @@ -84,6 +84,84 @@ export class TocsEventHandler implements vscode.TreeDragAndDropController<BookOr
await this.fireEvent(event)
}

async askTitle(title?: string): Promise<string | undefined> {
return await vscode.window.showInputBox({
prompt: 'Please enter the title',
value: title ?? '',
validateInput: text => {
return text.trim().length === 0 ? 'Title cannot be empty' : null
}
})
}

async addNode(nodeType: TocNodeKind, node: BookOrTocNode, slug: string | undefined) {
const title = await this.askTitle()
if (title === undefined) { return }
const bookIndex = this.tocTreesProvider.getParentBookIndex(node) ?? 0
if (nodeType === TocNodeKind.Page) {
const event: CreatePageEvent = {
type: TocNodeKind.Page,
title,
bookIndex
}
await this.fireEvent(event)
} else if (nodeType === TocNodeKind.Subbook) {
const event: CreateSubbookEvent = {
type: TocNodeKind.Subbook,
title,
slug,
bookIndex
}
await this.fireEvent(event)
} else if (nodeType === TocNodeKind.Ancillary) {
const event: CreateAncillaryEvent = {
type: TocNodeKind.Ancillary,
title,
slug,
bookIndex
}
await this.fireEvent(event)
}
}

async renameNode(node: BookOrTocNode) {
// TODO Implement the rename functionality using inline editing (wait for the API to be available)
// https://github.com/microsoft/vscode/issues/97190
// https://stackoverflow.com/questions/70594061/change-an-existing-label-name-in-tree-view-vscode-extension
const newTitle = await this.askTitle(('title' in node) ? node.title : '')
const nodeToken = expect(
getNodeToken(node),
'BUG: Could not get token of renamed node'
)
if (newTitle === undefined) { return }
const bookIndex = this.tocTreesProvider.getParentBookIndex(node) ?? 0
if (node.type === TocNodeKind.Subbook) {
const event: TocModification = {
type: TocModificationKind.SubbookRename,
newTitle,
nodeToken,
bookIndex
}
await this.fireEvent(event)
} else if (node.type === TocNodeKind.Page) {
const event: TocModification = {
type: TocModificationKind.PageRename,
newTitle,
nodeToken,
bookIndex
}
await this.fireEvent(event)
} else if (node.type === TocNodeKind.Ancillary) {
const event: TocModification = {
type: TocModificationKind.AncillaryRename,
newTitle,
nodeToken,
bookIndex
}
await this.fireEvent(event)
}
}

handleDrag(source: readonly BookOrTocNode[], dataTransfer: vscode.DataTransfer): void {
dataTransfer.set(XFER_ITEM_ID, new vscode.DataTransferItem(source[0]))
}
Expand Down
3 changes: 3 additions & 0 deletions client/static/xsd/cnxml.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@
</xs:restriction>
</xs:simpleType>
</xs:attribute>
<xs:attribute name="resource"/>
<xs:attribute name="document-link"/>
<xs:attribute name="ancillary-type"/>
</xs:complexType>
</xs:element>
<xs:group name="title">
Expand Down
32 changes: 26 additions & 6 deletions common/src/toc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,33 @@
// This enum is also hardcoded in the toc-editor Webview
export enum TocNodeKind {
Subbook = 'TocNodeKind.Subbook',
Page = 'TocNodeKind.Page'
Page = 'TocNodeKind.Page',
Ancillary = 'TocNodeKind.Ancillary'
}

export enum BookRootNode {
Singleton = 'BookRootNode.Singleton'
}

export enum TocModificationKind {
Add = 'TocModificationKind.Add',
Move = 'TocModificationKind.Move',
Remove = 'TocModificationKind.Remove',
PageRename = 'TocModificationKind.PageRename',
SubbookRename = 'TocModificationKind.SubbookRename',
AncillaryRename = 'TocModificationKind.AncillaryRename'
}

type TocNode<I, L> = TocSubbook<I, L> | TocPage<L>
type TocNode<I, L> = TocSubbook<I, L> | TocPage<L> | TocAncillary<L>
export interface TocSubbook<I, L> { readonly type: TocNodeKind.Subbook, children: Array<TocNode<I, L>>, value: I }
export interface TocPage<L> { readonly type: TocNodeKind.Page, value: L }
export interface TocAncillary<L> { readonly type: TocNodeKind.Ancillary, value: L }

export type Token = string
export interface ClientPageish { token: Token, title: string | undefined, fileId: string, absPath: string }
export interface ClientAncillaryish { token: Token, title: string | undefined, fileId: string, absPath: string }
export interface ClientSubbookish { token: Token, title: string }
export type ClientTocNode = TocNode<ClientSubbookish, ClientPageish>
export type ClientTocNode = TocNode<ClientSubbookish, (ClientPageish | ClientAncillaryish)>

export interface BookToc {
readonly type: BookRootNode.Singleton
Expand All @@ -38,9 +43,9 @@ export interface BookToc {

export interface TocModificationParams {
workspaceUri: string
event: TocModification | CreateSubbookEvent | CreatePageEvent
event: TocModification | CreateSubbookEvent | CreatePageEvent | CreateAncillaryEvent
}
export type TocModification = (TocMoveEvent | TocRemoveEvent | PageRenameEvent | SubbookRenameEvent)
export type TocModification = (TocMoveEvent | TocRemoveEvent | PageRenameEvent | SubbookRenameEvent | AncillaryRenameEvent)
export interface TocMoveEvent {
readonly type: TocModificationKind.Move
readonly nodeToken: Token
Expand All @@ -67,12 +72,27 @@ export interface SubbookRenameEvent {
readonly bookIndex: number
}

export interface AncillaryRenameEvent {
readonly type: TocModificationKind.AncillaryRename
readonly newTitle: string
readonly nodeToken: Token
readonly bookIndex: number
}

export interface CreateSubbookEvent {
readonly type: TocNodeKind.Subbook
readonly title: string
readonly slug: string
readonly slug: string | undefined
readonly bookIndex: number
}

export interface CreateAncillaryEvent {
readonly type: TocNodeKind.Ancillary
readonly title: string
readonly slug: string | undefined
readonly bookIndex: number
}

export interface CreatePageEvent {
readonly type: TocNodeKind.Page
readonly title: string
Expand Down
53 changes: 48 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,20 +67,44 @@
"title": "Generate README",
"category": "Openstax"
},
{
"command": "openstax.addAncillaryToToc",
"title": "Add Ancillary",
"category": "Openstax",
"icon": "$(person-add)"
},
{
"command": "openstax.addPageToToc",
"title": "Add Page",
"category": "Openstax",
"icon": "$(file-add)"
},
{
"command": "openstax.addSubBookToToc",
"title": "Add Sub Book",
"category": "Openstax",
"icon": "$(file-directory-create)"
},
{
"command": "openstax.toggleTocTreesFiltering",
"title": "Toggle ToC Filtering",
"category": "Openstax",
"icon": "$(filter)"
"icon": "$(expand-all)"
},
{
"command": "openstax.validateContent",
"title": "Validate Content",
"category": "Openstax"
},
{
"command": "openstax.test",
"title": "Validate Content",
"command": "openstax.renameNode",
"title": "Rename Node",
"category": "Openstax",
"icon": "$(pencil)"
},
{
"command": "openstax.removeNode",
"title": "Remove Node",
"category": "Openstax",
"icon": "$(trash)"
}
Expand Down Expand Up @@ -136,7 +160,7 @@
],
"commandPalette": [
{
"command": "openstax.showPreviewToSide",
"command": "openstax.showPreviewToSide",
"when": "editorLangId == xml && !notebookEditorFocused",
"group": "navigation"
}
Expand All @@ -146,11 +170,30 @@
"command": "openstax.toggleTocTreesFiltering",
"when": "view == tocTrees",
"group": "navigation"
},
{
"command": "openstax.addAncillaryToToc",
"when": "view == tocTrees",
"group": "navigation"
},
{
"command": "openstax.addSubBookToToc",
"when": "view == tocTrees",
"group": "navigation"
},
{
"command": "openstax.addPageToToc",
"when": "view == tocTrees",
"group": "navigation"
}
],
"view/item/context": [
{
"command": "openstax.test",
"command": "openstax.renameNode",
"when": "view == tocTrees && viewItem =~ /,?rename,?/",
"group": "inline"
},{
"command": "openstax.removeNode",
"when": "view == tocTrees && viewItem =~ /,?delete,?/",
"group": "inline"
}
Expand Down
2 changes: 1 addition & 1 deletion server/src/book-toc-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ function recTree(tocIdMap: IdMap<string, TocSubbookWithRange | PageNode>, parent
}

function recBuild(doc: Document, node: ClientTocNode): Element {
if (node.type === TocNodeKind.Page) {
if (node.type === TocNodeKind.Page || node.type === TocNodeKind.Ancillary) {
const ret = doc.createElementNS(NS_COLLECTION, 'col:module')
ret.setAttribute('document', node.value.fileId)
return ret
Expand Down
Loading

0 comments on commit fdd527c

Please sign in to comment.