@@ -253,11 +253,21 @@ export class KeyboardOnlyTreeEngine {
253253 const parent = parents [ 0 ] ;
254254 // 获取预方向
255255 const preDirection = this . getNodePreDirection ( parent ) ;
256+ // 自动命名新节点(如果当前选中的同级节点有标号特征。)
257+ let nextNodeName = "新节点" ;
258+ let isAddNewNumberName = false ;
259+ if ( currentSelectNode instanceof TextNode ) {
260+ const newName = extractNumberAndReturnNext ( currentSelectNode . text ) ;
261+ if ( newName ) {
262+ nextNodeName = newName ;
263+ isAddNewNumberName = true ;
264+ }
265+ }
256266 // 当前选择的节点的正下方创建一个节点
257267 // 找到创建点
258268 const newLocation = currentSelectNode . collisionBox . getRectangle ( ) . leftBottom . add ( new Vector ( 0 , 1 ) ) ;
259269 const newNode = new TextNode ( this . project , {
260- text : "新节点" ,
270+ text : nextNodeName ,
261271 details : [ ] ,
262272 uuid : v4 ( ) ,
263273 collisionBox : new CollisionBox ( [
@@ -316,7 +326,7 @@ export class KeyboardOnlyTreeEngine {
316326 setTimeout (
317327 ( ) => {
318328 // 防止把反引号给输入进去
319- this . project . controllerUtils . editTextNode ( newNode ) ;
329+ this . project . controllerUtils . editTextNode ( newNode , ! isAddNewNumberName ) ;
320330 } ,
321331 ( 1000 / 60 ) * 6 ,
322332 ) ;
@@ -343,3 +353,107 @@ export class KeyboardOnlyTreeEngine {
343353 // TODO
344354 }
345355}
356+
357+ /**
358+ * 提取字符串中的标号格式并返回下一标号字符串
359+ * 例如:
360+ * 输入:"1 xxxx"
361+ * 返回:"2 "
362+ *
363+ * 输入:"1. xxxx"
364+ * 返回:"2. "
365+ *
366+ * 输入:"[1] xxx"
367+ * 返回:"[2] "
368+ *
369+ * 输入:"1) xxx"
370+ * 返回:"2) "
371+ *
372+ * 输入:"(1) xxx"
373+ * 返回:"(2) "
374+ *
375+ * 类似的括号格式可能还有:
376+ * 【1】
377+ * 1:
378+ * 1、
379+ * 1,
380+ * 1,
381+ *
382+ * 总之,返回的序号总比输入的序号大1
383+ * 输入的序号后面可能会有标题内容,返回的内容中会不带标题内容,自动过滤
384+ * @param str
385+ */
386+ function extractNumberAndReturnNext ( str : string ) : string {
387+ const s = ( str ?? "" ) . trimStart ( ) ;
388+ if ( ! s ) return "" ;
389+
390+ // 成对括号映射:ASCII + 常见全角/中文括号(用 Unicode 转义避免混淆)
391+ const BRACKET_PAIRS : Record < string , string > = {
392+ "(" : ")" ,
393+ "[" : "]" ,
394+ "{" : "}" ,
395+ "\uFF08" : "\uFF09" , // ( )
396+ "\u3010" : "\u3011" , // 【 】
397+ "\u3014" : "\u3015" , // 〔 〕
398+ "\u3016" : "\u3017" , // 〖 〗
399+ } ;
400+
401+ // 常见分隔符集合:半角 + 全角(用 Unicode 转义避免混淆)
402+ const DELIMS = new Set < string > ( [
403+ "." ,
404+ ")" ,
405+ ":" ,
406+ "," ,
407+ "\uFF1A" , // :
408+ "\u3001" , // 、
409+ "\uFF0C" , // ,
410+ "\uFF0E" , // .
411+ "\u3002" , // 。
412+ ] ) ;
413+
414+ // 1) 括号包裹的数字:例如 (1)、[1]、{1}、(1)、【1】、〔1〕、〖1〗
415+ const open = s [ 0 ] ;
416+ const close = BRACKET_PAIRS [ open ] ;
417+ if ( close ) {
418+ let i = 1 ;
419+
420+ // 跳过括号后的空白
421+ while ( i < s . length && / \s / . test ( s [ i ] ) ) i ++ ;
422+
423+ // 读取连续数字
424+ const numStart = i ;
425+ while ( i < s . length && s . charCodeAt ( i ) >= 48 && s . charCodeAt ( i ) <= 57 ) i ++ ;
426+ if ( i === numStart ) return "" ; // 括号里没数字
427+
428+ // 跳过数字后的空白
429+ while ( i < s . length && / \s / . test ( s [ i ] ) ) i ++ ;
430+
431+ // 必须紧跟对应闭括号
432+ if ( s [ i ] === close ) {
433+ const n = parseInt ( s . slice ( numStart , i ) , 10 ) + 1 ;
434+ return `${ open } ${ n } ${ close } ` ;
435+ }
436+ return "" ;
437+ }
438+
439+ // 2) 数字起始的情况:1. / 1) / 1: / 1、 / 1, / 1,/ 1. / 1。/ 或纯数字“1 ”
440+ const m = s . match ( / ^ ( \d + ) / ) ;
441+ if ( m ) {
442+ const numStr = m [ 1 ] ;
443+ let i = numStr . length ;
444+
445+ // 跳过数字后的空白
446+ while ( i < s . length && / \s / . test ( s [ i ] ) ) i ++ ;
447+
448+ const delim = s [ i ] ;
449+ const next = String ( parseInt ( numStr , 10 ) + 1 ) ;
450+
451+ if ( delim && DELIMS . has ( delim ) ) {
452+ return `${ next } ${ delim } ` ;
453+ }
454+ return `${ next } ` ;
455+ }
456+
457+ // 未匹配到已知格式
458+ return "" ;
459+ }
0 commit comments