@@ -24,6 +24,7 @@ import { PathString } from "@/utils/pathString";
2424import { DateChecker } from "@/utils/dateChecker" ;
2525import { TextNodeSmartTools } from "@/core/service/dataManageService/textNodeSmartTools" ;
2626import { ReferenceManager } from "@/core/stage/stageManager/concreteMethods/StageReferenceManager" ;
27+ import _ from "lodash" ;
2728
2829/**
2930 * 这里是专门存放代码相同的地方
@@ -394,33 +395,117 @@ export class ControllerUtils {
394395 ) {
395396 // 处理#开头的逻辑节点补全
396397 if ( text . startsWith ( "#" ) ) {
397- // 提取搜索文本,去掉所有#
398- const searchText = text . replaceAll ( "#" , "" ) . toLowerCase ( ) ;
399-
400- const logicNodeEntries = Object . entries ( LogicNodeNameToRenderNameMap ) . map ( ( [ key , renderName ] ) => ( {
401- key,
402- name : key . replaceAll ( "#" , "" ) . toLowerCase ( ) ,
403- renderName,
404- } ) ) ;
405-
406- const fuse = new Fuse ( logicNodeEntries , {
407- keys : [ "name" ] ,
408- threshold : 0.3 , // (0 = exact, 1 = very fuzzy)
398+ this . handleAutoCompleteLogic ( text , node , ele , setWindowId ) ;
399+ // 处理[[格式的补全
400+ } else if ( text . startsWith ( "[[" ) ) {
401+ this . handleAutoCompleteReferenceDebounced ( text , node , ele , setWindowId ) ;
402+ }
403+ }
404+ private handleAutoCompleteReferenceDebounced = _ . debounce (
405+ ( text : string , node : TextNode , ele : HTMLTextAreaElement , setWindowId : ( id : string ) => void ) => {
406+ this . handleAutoCompleteReference ( text , node , ele , setWindowId ) ;
407+ console . log ( "ref匹配执行了" ) ;
408+ } ,
409+ 500 ,
410+ ) ;
411+
412+ private handleAutoCompleteLogic (
413+ text : string ,
414+ node : TextNode ,
415+ ele : HTMLTextAreaElement ,
416+ setWindowId : ( id : string ) => void ,
417+ ) {
418+ // 提取搜索文本,去掉所有#
419+ const searchText = text . replaceAll ( "#" , "" ) . toLowerCase ( ) ;
420+
421+ const logicNodeEntries = Object . entries ( LogicNodeNameToRenderNameMap ) . map ( ( [ key , renderName ] ) => ( {
422+ key,
423+ name : key . replaceAll ( "#" , "" ) . toLowerCase ( ) ,
424+ renderName,
425+ } ) ) ;
426+
427+ const fuse = new Fuse ( logicNodeEntries , {
428+ keys : [ "name" ] ,
429+ threshold : 0.3 , // (0 = exact, 1 = very fuzzy)
430+ } ) ;
431+
432+ const searchResults = fuse . search ( searchText ) ;
433+ const matchingNodes = searchResults . map ( ( result ) => [ result . item . key , result . item . renderName ] ) ;
434+
435+ // 打开自动补全窗口
436+ if ( this . currentAutoCompleteWindowId ) {
437+ SubWindow . close ( this . currentAutoCompleteWindowId ) ;
438+ }
439+ if ( matchingNodes . length > 0 ) {
440+ const windowId = AutoCompleteWindow . open (
441+ this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
442+ Object . fromEntries ( matchingNodes ) ,
443+ ( value ) => {
444+ ele . value = value ;
445+ } ,
446+ ) . id ;
447+ this . currentAutoCompleteWindowId = windowId ;
448+ setWindowId ( windowId ) ;
449+ } else {
450+ const windowId = AutoCompleteWindow . open (
451+ this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
452+ {
453+ tip :
454+ searchText === "" ? "暂无匹配的逻辑节点名称,请输入全大写字母" : `暂无匹配的逻辑节点名称【${ searchText } 】` ,
455+ } ,
456+ ( value ) => {
457+ ele . value = value ;
458+ } ,
459+ ) . id ;
460+ this . currentAutoCompleteWindowId = windowId ;
461+ setWindowId ( windowId ) ;
462+ }
463+ }
464+
465+ private async handleAutoCompleteReference (
466+ text : string ,
467+ node : TextNode ,
468+ ele : HTMLTextAreaElement ,
469+ setWindowId : ( id : string ) => void ,
470+ ) {
471+ // 提取搜索文本,去掉开头的[[
472+ const searchText = text . slice ( 2 ) . toLowerCase ( ) . replace ( "]]" , "" ) ;
473+ // 检查是否包含#
474+ const hasHash = searchText . includes ( "#" ) ;
475+
476+ if ( ! hasHash ) {
477+ // 获取最近文件列表
478+ const recentFiles = await RecentFileManager . getRecentFiles ( ) ;
479+
480+ // 处理最近文件列表,提取文件名
481+ const fileEntries = recentFiles . map ( ( file ) => {
482+ // 提取文件名(不含扩展名)
483+ const fileName = PathString . getFileNameFromPath ( file . uri . path ) ;
484+ return { name : fileName , time : file . time } ; // 使用对象格式以便Fuse.js搜索
485+ } ) ;
486+
487+ const fuse = new Fuse ( fileEntries , {
488+ keys : [ "name" ] , // 搜索name属性
489+ threshold : 0.3 ,
409490 } ) ;
410491
411492 const searchResults = fuse . search ( searchText ) ;
412- const matchingNodes = searchResults . map ( ( result ) => [ result . item . key , result . item . renderName ] ) ;
493+ const matchingFiles = searchResults . map ( ( result ) => [
494+ result . item . name ,
495+ DateChecker . formatRelativeTime ( result . item . time ) ,
496+ ] ) ; // 转换为相对时间格式
413497
414498 // 打开自动补全窗口
415499 if ( this . currentAutoCompleteWindowId ) {
416500 SubWindow . close ( this . currentAutoCompleteWindowId ) ;
417501 }
418- if ( matchingNodes . length > 0 ) {
502+ if ( matchingFiles . length > 0 ) {
419503 const windowId = AutoCompleteWindow . open (
420504 this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
421- Object . fromEntries ( matchingNodes ) ,
505+ Object . fromEntries ( matchingFiles ) ,
422506 ( value ) => {
423- ele . value = value ;
507+ // 用户选择后,需要保留[[前缀并添加选择的文件名
508+ ele . value = `[[${ value } ` ;
424509 } ,
425510 ) . id ;
426511 this . currentAutoCompleteWindowId = windowId ;
@@ -429,126 +514,65 @@ export class ControllerUtils {
429514 const windowId = AutoCompleteWindow . open (
430515 this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
431516 {
432- tip :
433- searchText === ""
434- ? "暂无匹配的逻辑节点名称,请输入全大写字母"
435- : `暂无匹配的逻辑节点名称【${ searchText } 】` ,
517+ tip : searchText === "" ? "暂无最近文件" : `暂无匹配的最近文件【${ searchText } 】` ,
436518 } ,
437519 ( value ) => {
438- ele . value = value ;
520+ ele . value = `[[ ${ value } ` ;
439521 } ,
440522 ) . id ;
441523 this . currentAutoCompleteWindowId = windowId ;
442524 setWindowId ( windowId ) ;
443525 }
444- // 处理[[格式的补全
445- } else if ( text . startsWith ( "[[" ) ) {
446- // 提取搜索文本,去掉开头的[[
447- const searchText = text . slice ( 2 ) . toLowerCase ( ) . replace ( "]]" , "" ) ;
448- // 检查是否包含#
449- const hasHash = searchText . includes ( "#" ) ;
450-
451- if ( ! hasHash ) {
452- // 获取最近文件列表
453- const recentFiles = await RecentFileManager . getRecentFiles ( ) ;
454-
455- // 处理最近文件列表,提取文件名
456- const fileEntries = recentFiles . map ( ( file ) => {
457- // 提取文件名(不含扩展名)
458- const fileName = PathString . getFileNameFromPath ( file . uri . path ) ;
459- return { name : fileName , time : file . time } ; // 使用对象格式以便Fuse.js搜索
460- } ) ;
526+ } else {
527+ // 包含#,拆分文件名和section名称
528+ const [ fileName , sectionName ] = searchText . split ( "#" , 2 ) ;
461529
462- const fuse = new Fuse ( fileEntries , {
463- keys : [ "name" ] , // 搜索name属性
464- threshold : 0.3 ,
465- } ) ;
530+ // 获取该文件中的所有section
531+ const sections = await CrossFileContentQuery . getSectionsByFileName ( fileName ) ;
466532
467- const searchResults = fuse . search ( searchText ) ;
468- const matchingFiles = searchResults . map ( ( result ) => [
469- result . item . name ,
470- DateChecker . formatRelativeTime ( result . item . time ) ,
471- ] ) ; // 转换为相对时间格式
533+ // 将section名称转换为对象数组,以便Fuse.js搜索
534+ const sectionObjects = sections . map ( ( section ) => ( { name : section } ) ) ;
535+ let searchResults ;
472536
473- // 打开自动补全窗口
474- if ( this . currentAutoCompleteWindowId ) {
475- SubWindow . close ( this . currentAutoCompleteWindowId ) ;
476- }
477- if ( matchingFiles . length > 0 ) {
478- const windowId = AutoCompleteWindow . open (
479- this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
480- Object . fromEntries ( matchingFiles ) ,
481- ( value ) => {
482- // 用户选择后,需要保留[[前缀并添加选择的文件名
483- ele . value = `[[${ value } ` ;
484- } ,
485- ) . id ;
486- this . currentAutoCompleteWindowId = windowId ;
487- setWindowId ( windowId ) ;
488- } else {
489- const windowId = AutoCompleteWindow . open (
490- this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
491- {
492- tip : searchText === "" ? "暂无最近文件" : `暂无匹配的最近文件【${ searchText } 】` ,
493- } ,
494- ( value ) => {
495- ele . value = `[[${ value } ` ;
496- } ,
497- ) . id ;
498- this . currentAutoCompleteWindowId = windowId ;
499- setWindowId ( windowId ) ;
500- }
537+ // 当section名称为空时,显示所有section(最多20个)
538+ if ( ! sectionName ?. trim ( ) ) {
539+ // 取前20个section
540+ searchResults = sectionObjects . slice ( 0 , 20 ) . map ( ( item ) => ( { item } ) ) ;
501541 } else {
502- // 包含#,拆分文件名和section名称
503- const [ fileName , sectionName ] = searchText . split ( "#" , 2 ) ;
504-
505- // 获取该文件中的所有section
506- const sections = await CrossFileContentQuery . getSectionsByFileName ( fileName ) ;
507-
508- // 将section名称转换为对象数组,以便Fuse.js搜索
509- const sectionObjects = sections . map ( ( section ) => ( { name : section } ) ) ;
510- let searchResults ;
511-
512- // 当section名称为空时,显示所有section(最多20个)
513- if ( ! sectionName ?. trim ( ) ) {
514- // 取前20个section
515- searchResults = sectionObjects . slice ( 0 , 20 ) . map ( ( item ) => ( { item } ) ) ;
516- } else {
517- // 创建Fuse搜索器,对section名称进行模糊匹配
518- const fuse = new Fuse ( sectionObjects , { keys : [ "name" ] , threshold : 0.3 } ) ;
519- searchResults = fuse . search ( sectionName ) ;
520- }
542+ // 创建Fuse搜索器,对section名称进行模糊匹配
543+ const fuse = new Fuse ( sectionObjects , { keys : [ "name" ] , threshold : 0.3 } ) ;
544+ searchResults = fuse . search ( sectionName ) ;
545+ }
521546
522- const matchingSections = searchResults . map ( ( result ) => [ result . item . name , "" ] ) ;
547+ const matchingSections = searchResults . map ( ( result ) => [ result . item . name , "" ] ) ;
523548
524- // 打开自动补全窗口
525- if ( this . currentAutoCompleteWindowId ) {
526- SubWindow . close ( this . currentAutoCompleteWindowId ) ;
527- }
528- if ( matchingSections . length > 0 ) {
529- const windowId = AutoCompleteWindow . open (
530- this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
531- Object . fromEntries ( matchingSections ) ,
532- ( value ) => {
533- // 用户选择后,需要保留[[前缀、文件名和#,并添加选择的section名称
534- ele . value = `[[${ fileName } #${ value } ` ;
535- } ,
536- ) . id ;
537- this . currentAutoCompleteWindowId = windowId ;
538- setWindowId ( windowId ) ;
539- } else {
540- const windowId = AutoCompleteWindow . open (
541- this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
542- {
543- tip : sectionName === "" ? `这个文件中没有section,无法创建引用` : `暂无匹配的section【${ sectionName } 】` ,
544- } ,
545- ( value ) => {
546- ele . value = `[[${ fileName } #${ value } ` ;
547- } ,
548- ) . id ;
549- this . currentAutoCompleteWindowId = windowId ;
550- setWindowId ( windowId ) ;
551- }
549+ // 打开自动补全窗口
550+ if ( this . currentAutoCompleteWindowId ) {
551+ SubWindow . close ( this . currentAutoCompleteWindowId ) ;
552+ }
553+ if ( matchingSections . length > 0 ) {
554+ const windowId = AutoCompleteWindow . open (
555+ this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
556+ Object . fromEntries ( matchingSections ) ,
557+ ( value ) => {
558+ // 用户选择后,需要保留[[前缀、文件名和#,并添加选择的section名称
559+ ele . value = `[[${ fileName } #${ value } ` ;
560+ } ,
561+ ) . id ;
562+ this . currentAutoCompleteWindowId = windowId ;
563+ setWindowId ( windowId ) ;
564+ } else {
565+ const windowId = AutoCompleteWindow . open (
566+ this . project . renderer . transformWorld2View ( node . rectangle ) . leftBottom ,
567+ {
568+ tip : sectionName === "" ? `这个文件中没有section,无法创建引用` : `暂无匹配的section【${ sectionName } 】` ,
569+ } ,
570+ ( value ) => {
571+ ele . value = `[[${ fileName } #${ value } ` ;
572+ } ,
573+ ) . id ;
574+ this . currentAutoCompleteWindowId = windowId ;
575+ setWindowId ( windowId ) ;
552576 }
553577 }
554578 }
0 commit comments