@@ -7,6 +7,7 @@ class BlockTreeNode {
77 public children : BlockTreeNode [ ] = [ ] ;
88 public content : string = "" ;
99 public properties : Record < string , any > = { } ;
10+ public headerLevel = 0 ;
1011 public blankLevel = 0 ;
1112}
1213
@@ -65,12 +66,29 @@ async function splitBlocksToTree(blockEntities: BlockEntity[], transformerContex
6566 // inherent refBlock properties
6667 blockTreeNode . properties = convertBlockProperties ( blockEntity . properties ) ;
6768 }
68- // remove lower blank level
69+
70+ // Find correct parent
6971 for ( let j = lastBlockTreeNodes . length - 1 ; j >= 0 ; j -- ) {
70- if ( lastBlockTreeNodes [ j ] . blankLevel >= blockTreeNode . blankLevel ) {
71- lastBlockTreeNodes . pop ( ) ;
72+ const parentCandidate = lastBlockTreeNodes [ j ] ;
73+ if ( blockTreeNode . headerLevel > 0 ) { // Current is a header
74+ if ( parentCandidate . headerLevel > 0 && parentCandidate . headerLevel >= blockTreeNode . headerLevel ) {
75+ lastBlockTreeNodes . pop ( ) ;
76+ } else if ( parentCandidate . headerLevel === 0 ) { // if parent candidate is not a header, pop it to find a header parent
77+ lastBlockTreeNodes . pop ( ) ;
78+ } else {
79+ break ;
80+ }
81+ } else { // Current is not a header
82+ if ( parentCandidate . headerLevel === 0 && parentCandidate . blankLevel >= blockTreeNode . blankLevel ) {
83+ lastBlockTreeNodes . pop ( ) ;
84+ } else if ( parentCandidate . headerLevel > 0 && blockTreeNode . headerLevel === 0 ) {
85+ break ;
86+ } else {
87+ break ;
88+ }
7289 }
7390 }
91+
7492 if ( lastBlockTreeNodes . length == 0 ) {
7593 // append output
7694 outputBlockTreeNodes . push ( blockTreeNode ) ;
@@ -118,27 +136,7 @@ async function splitBlocksToTree(blockEntities: BlockEntity[], transformerContex
118136
119137
120138 // handle lines
121- // handle table
122- if ( line [ blankNum ] === '|' ) {
123- let combineTable = false ;
124- if ( lastBlockTreeNodes . length > 0 ) {
125- let lastNodeTrim = lastBlockTreeNodes [ lastBlockTreeNodes . length - 1 ] . content . trim ( ) ;
126- if ( lastNodeTrim . length > 0 && lastNodeTrim [ 0 ] === '|' ) {
127- lastBlockTreeNodes [ lastBlockTreeNodes . length - 1 ] . content += '\n' + line ;
128- combineTable = true ;
129- }
130- }
131- if ( ! combineTable ) {
132- let blockTreeNode = {
133- refBlock : undefined ,
134- content : line ,
135- children : [ ] ,
136- properties : { } ,
137- blankLevel : blankLevel
138- }
139- is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
140- }
141- } else if ( ! completeCodeBlock ) {
139+ if ( ! completeCodeBlock ) {
142140 const position = line . indexOf ( '```' )
143141 if ( position >= 0 ) {
144142 lastBlockTreeNodes [ lastBlockTreeNodes . length - 1 ] . content += '\n' + line . substring ( 0 , position + 3 )
@@ -182,6 +180,7 @@ async function splitBlocksToTree(blockEntities: BlockEntity[], transformerContex
182180 content : line1 ,
183181 children : [ ] ,
184182 properties : { } ,
183+ headerLevel : 0 ,
185184 blankLevel : blankLevel
186185 }
187186 is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
@@ -200,47 +199,87 @@ async function splitBlocksToTree(blockEntities: BlockEntity[], transformerContex
200199 content : line ,
201200 children : [ ] ,
202201 properties : { } ,
202+ headerLevel : 0 ,
203203 blankLevel : blankLevel
204204 }
205205 is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
206206 }
207- }
208- // handle order list
209- else if ( / ^ \s * ( [ 0 - 9 ] + | [ A - z ] + | [ 一 二 三 四 五 六 七 八 九 十 零 ] + ) [ . 、 . ] \s * / . test ( line ) ) {
210- let blockProperties : { [ key : string ] : string } = { } ;
211- if ( ! transformerContext . orderedToNonOrdered ) {
212- blockProperties [ 'logseq.order-list-type' ] = 'number' ;
207+ } else {
208+ // handle header
209+ let match = line . match ( / ^ \s * ( # + ) \s ( .* ) $ / ) ;
210+ if ( match ) {
211+ let blockTreeNode = {
212+ refBlock : undefined ,
213+ content : match [ 1 ] + ' ' + match [ 2 ] ,
214+ children : [ ] ,
215+ properties : { heading : match [ 1 ] . length } ,
216+ headerLevel : match [ 1 ] . length ,
217+ blankLevel : blankLevel
218+ } ;
219+ is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
220+ }
221+ // handle table
222+ else if ( line [ blankNum ] === '|' ) {
223+ let combineTable = false ;
224+ if ( lastBlockTreeNodes . length > 0 ) {
225+ let lastNodeTrim = lastBlockTreeNodes [ lastBlockTreeNodes . length - 1 ] . content . trim ( ) ;
226+ if ( lastNodeTrim . length > 0 && lastNodeTrim [ 0 ] === '|' ) {
227+ lastBlockTreeNodes [ lastBlockTreeNodes . length - 1 ] . content += '\n' + line ;
228+ combineTable = true ;
229+ }
230+ }
231+ if ( ! combineTable ) {
232+ let blockTreeNode = {
233+ refBlock : undefined ,
234+ content : line ,
235+ children : [ ] ,
236+ properties : { } ,
237+ headerLevel : 0 ,
238+ blankLevel : blankLevel
239+ }
240+ is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
241+ }
242+ }
243+ // handle order list
244+ else if ( / ^ \s * ( [ 0 - 9 ] + | [ A - z ] + | [ 一 二 三 四 五 六 七 八 九 十 零 ] + ) [ . 、 . ] \s * / . test ( line ) ) {
245+ let blockProperties : { [ key : string ] : string } = { } ;
246+ if ( ! transformerContext . orderedToNonOrdered ) {
247+ blockProperties [ 'logseq.order-list-type' ] = 'number' ;
248+ }
249+ let blockTreeNode = {
250+ refBlock : undefined ,
251+ content : line . replace ( / ^ \s * ( [ 0 - 9 ] + | [ A - z ] + | [ 一 二 三 四 五 六 七 八 九 十 零 ] + ) [ . 、 . ] \s * / , '' ) ,
252+ children : [ ] ,
253+ properties : blockProperties ,
254+ headerLevel : 0 ,
255+ blankLevel : blankLevel
256+ } ;
257+ is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
258+ }
259+ // handle normal list
260+ else if ( / ^ \s * [ - * ] \s / . test ( line ) ) {
261+ let blockTreeNode = {
262+ refBlock : undefined ,
263+ content : line . replace ( / ^ \s * [ - * ] \s / , '' ) ,
264+ children : [ ] ,
265+ properties : { } ,
266+ headerLevel : 0 ,
267+ blankLevel : blankLevel
268+ } ;
269+ is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
270+ }
271+ // handle normal line
272+ else {
273+ let blockTreeNode = {
274+ refBlock : undefined ,
275+ content : line ,
276+ children : [ ] ,
277+ properties : { } ,
278+ headerLevel : 0 ,
279+ blankLevel : blankLevel
280+ } ;
281+ is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
213282 }
214- let blockTreeNode = {
215- refBlock : undefined ,
216- content : line . replace ( / ^ \s * ( [ 0 - 9 ] + | [ A - z ] + | [ 一 二 三 四 五 六 七 八 九 十 零 ] + ) [ . 、 . ] \s * / , '' ) ,
217- children : [ ] ,
218- properties : blockProperties ,
219- blankLevel : blankLevel
220- } ;
221- is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
222- }
223- // handle normal list
224- else if ( / ^ \s * [ - * ] \s / . test ( line ) ) {
225- let blockTreeNode = {
226- refBlock : undefined ,
227- content : line . replace ( / ^ \s * [ - * ] \s / , '' ) ,
228- children : [ ] ,
229- properties : { } ,
230- blankLevel : blankLevel
231- } ;
232- is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
233- }
234- // handle normal line
235- else {
236- let blockTreeNode = {
237- refBlock : undefined ,
238- content : line ,
239- children : [ ] ,
240- properties : { } ,
241- blankLevel : blankLevel
242- } ;
243- is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
244283 }
245284 }
246285 // append empty line for empty result
@@ -250,6 +289,7 @@ async function splitBlocksToTree(blockEntities: BlockEntity[], transformerContex
250289 content : '' ,
251290 children : [ ] ,
252291 properties : { } ,
292+ headerLevel : 0 ,
253293 blankLevel : 0
254294 }
255295 is_first = appendNewBlockTreeNode ( blockTreeNode , is_first , blockEntity , lastBlockTreeNodes ) ;
@@ -300,6 +340,25 @@ function getContent(blockEntity: BlockEntity) {
300340
301341
302342async function headerModeAction ( blockEntities : BlockEntity [ ] , transformerContext : TransformerContext , headerLevel = - 1 ) {
343+ let boldToHeader = transformerContext . boldToHeader ;
344+
345+ function test_bold_header ( content : string ) {
346+ return ! / [ \r \n ] / . test ( content ) && / ^ \s * \* \* ( [ ^ * ] + ) \* \* [ , . : ; ! ? : \s , 。 : ; ! ? : ] * $ / . test ( content ) ;
347+ }
348+
349+ if ( boldToHeader && blockEntities . length > 0 ) {
350+ const boldBlocks = blockEntities . filter ( b => / \* \* ( .* ) \* \* / . test ( getContent ( b ) ) ) ;
351+ if ( boldBlocks . length > 0 ) {
352+ const convertibleBoldBlocksCount = boldBlocks . filter ( b => {
353+ const content = getContent ( b ) ;
354+ return test_bold_header ( content ) ;
355+ } ) . length ;
356+ if ( convertibleBoldBlocksCount < boldBlocks . length ) {
357+ boldToHeader = false ;
358+ }
359+ }
360+ }
361+
303362 for ( let blockEntity of blockEntities ) {
304363 if ( headerLevel < 0 ) {
305364 headerLevel = await getHeaderLevelByParent ( blockEntity ) ;
@@ -309,7 +368,7 @@ async function headerModeAction(blockEntities: BlockEntity[], transformerContext
309368 // is header
310369 let content = getContent ( blockEntity ) ;
311370 let is_header = ! / [ \r \n ] / . test ( content ) && / ^ \s * # + \s / . test ( content ) ;
312- let is_bold_header = transformerContext . boldToHeader && ! / [ \r \n ] / . test ( content ) && / ^ \s * \* \* ( [ ^ * ] + ) \* \* [ , . : ; ! ? : \s , 。 : ; ! ? : ] * $ / . test ( content )
371+ let is_bold_header = boldToHeader && test_bold_header ( content )
313372 if ( is_header || is_bold_header ) {
314373 let newContent = content ;
315374 newContent = newContent . replace ( / ^ \s * # + \s / , "" ) ;
@@ -320,16 +379,18 @@ async function headerModeAction(blockEntities: BlockEntity[], transformerContext
320379 newContent = newContent . replace ( / [ , . : ; ! ? : \s , 。 : ; ! ? : ] * $ / , "" ) ;
321380 }
322381 // remove serial number
323- if ( transformerContext . orderedToNonOrdered ) {
382+ if ( transformerContext . orderedToNonOrdered ) {
324383 newContent = newContent . replace ( / ^ \s * ( [ 0 - 9 ] + | [ A - z ] + | [ 一 二 三 四 五 六 七 八 九 十 零 ] + ) [ . 、 . ] \s * ( .* ) $ / , "$2" ) ;
325384 }
326385 // add header by header level
327386 newContent = " " + newContent . trim ( ) ;
328387 for ( let i = 0 ; i < headerLevel ; i ++ ) {
329388 newContent = '#' + newContent ;
330389 }
331- if ( content !== newContent ) {
332- await logseq . Editor . updateBlock ( blockEntity . uuid , newContent , { properties : convertBlockProperties ( blockEntity . properties ) } ) ;
390+ const newProperties = convertBlockProperties ( blockEntity . properties ) ;
391+ newProperties . heading = headerLevel ;
392+ if ( content !== newContent || ! isEqual ( newProperties , convertBlockProperties ( blockEntity . properties ) ) ) {
393+ await logseq . Editor . updateBlock ( blockEntity . uuid , newContent , { properties : newProperties } ) ;
333394 }
334395 }
335396 let children = await getBlockEntityChildren ( blockEntity ) ;
@@ -540,6 +601,7 @@ export async function transformAction(selectedBlockEntities: BlockEntity[]) {
540601 await headerModeAction ( selectedBlockEntities , transformerContext ) ;
541602 }
542603}
604+
543605async function updateHeadingProperty ( blockUuid : BlockUUID , level : number ) {
544606 if ( level > 0 ) {
545607 await logseq . Editor . upsertBlockProperty ( blockUuid , 'heading' , level ) ;
0 commit comments