- หน้าแรก
- Documentation
- เนทีฟ
- ไปป์ไลน์ข้อความ/การค้นหาแบบเนทีฟ
ไปป์ไลน์ข้อความ/การค้นหาแบบเนทีฟ
เอกสารนี้แมปพื้นผิวข้อความ/การค้นหาของ @f5-sales-demo/pi-natives (grep, glob, text, highlight) จาก TypeScript wrappers ไปยัง Rust N-API exports และกลับมาเป็น JS result objects
คำศัพท์ตาม docs/natives-architecture.md:
- Wrapper: TS API ใน
packages/natives/src/* - Rust module layer: N-API exports ใน
crates/pi-natives/src/* - Shared scan cache: แคชรายการไดเรกทอรีที่รองรับโดย
fs_cacheซึ่งใช้โดยกระบวนการค้นพบ/ค้นหา
ไฟล์การนำไปใช้งาน
หัวข้อที่มีชื่อว่า “ไฟล์การนำไปใช้งาน”packages/natives/src/grep/index.tspackages/natives/src/grep/types.tspackages/natives/src/glob/index.tspackages/natives/src/glob/types.tspackages/natives/src/text/index.tspackages/natives/src/text/types.tspackages/natives/src/highlight/index.tspackages/natives/src/highlight/types.tscrates/pi-natives/src/grep.rscrates/pi-natives/src/glob.rscrates/pi-natives/src/glob_util.rscrates/pi-natives/src/fs_cache.rscrates/pi-natives/src/text.rscrates/pi-natives/src/highlight.rscrates/pi-natives/src/fd.rs
การแมป JS API ↔ Rust export
หัวข้อที่มีชื่อว่า “การแมป JS API ↔ Rust export”| JS wrapper API | Rust export (#[napi], snake_case -> camelCase) | Rust module |
|---|---|---|
grep(options, onMatch?) | grep | grep.rs |
searchContent(content, options) | search | grep.rs |
hasMatch(content, pattern, options?) | hasMatch | grep.rs |
fuzzyFind(options) | fuzzyFind | fd.rs |
glob(options, onMatch?) | glob | glob.rs |
invalidateFsScanCache(path?) | invalidateFsScanCache | fs_cache.rs |
wrapTextWithAnsi(text, width) | wrapTextWithAnsi | text.rs |
truncateToWidth(text, maxWidth, ellipsis, pad) | truncateToWidth | text.rs |
sliceWithWidth(line, startCol, length, strict?) | sliceWithWidth | text.rs |
extractSegments(line, beforeEnd, afterStart, afterLen, strictAfter) | extractSegments | text.rs |
sanitizeText(text) | sanitizeText | text.rs |
visibleWidth(text) | visibleWidth | text.rs |
highlightCode(code, lang, colors) | highlightCode | highlight.rs |
supportsLanguage(lang) | supportsLanguage | highlight.rs |
getSupportedLanguages() | getSupportedLanguages | highlight.rs |
ภาพรวมไปป์ไลน์แยกตามระบบย่อย
หัวข้อที่มีชื่อว่า “ภาพรวมไปป์ไลน์แยกตามระบบย่อย”1) การค้นหาด้วย Regex (grep, searchContent, hasMatch)
หัวข้อที่มีชื่อว่า “1) การค้นหาด้วย Regex (grep, searchContent, hasMatch)”กระบวนการรับ input/options
หัวข้อที่มีชื่อว่า “กระบวนการรับ input/options”- TS wrapper ส่ง options ไปยัง native:
grep/index.tsส่งoptionsไปเกือบไม่เปลี่ยนแปลง และห่อ callback จาก(match) => voidให้เป็น napi threadsafe callback shape(err, match)searchContentและhasMatchส่ง string/Uint8Arrayโดยตรง
- Rust option structs ใน
grep.rsแปลงฟิลด์ camelCase (ignoreCase,maxCount,contextBefore,contextAfter,maxColumns,timeoutMs) grepสร้างCancelTokenจากtimeoutMs+AbortSignalและรันภายในtask::blocking("grep", ...)
สาขาการประมวลผล
หัวข้อที่มีชื่อว่า “สาขาการประมวลผล”- สาขาในหน่วยความจำ (pure utility)
search→search_sync→run_searchบนไบต์เนื้อหาที่ให้มา- ไม่มีการสแกนระบบไฟล์ ไม่ใช้
fs_cache
- สาขาไฟล์เดียว (ขึ้นอยู่กับระบบไฟล์)
grep_syncแก้ไข path, ตรวจสอบ metadata ว่าเป็นไฟล์, สตรีมสูงสุดMAX_FILE_BYTESต่อไฟล์ (4 MiB) ผ่าน ripgrep matcher
- สาขาไดเรกทอรี (ขึ้นอยู่กับระบบไฟล์)
- ค้นหาแคชเสริมผ่าน
fs_cache::get_or_scanเมื่อcache: true - สแกนใหม่ผ่าน
fs_cache::force_rescanเมื่อcache: false - ตรวจสอบผลลัพธ์ว่างซ้ำเสริมเมื่ออายุแคชเกิน
empty_recheck_ms() - การกรอง entry: เฉพาะไฟล์ + ตัวกรอง glob เสริม (
glob_util) + ตัวกรองประเภทเสริม (js,ts,rustเป็นต้น)
- ค้นหาแคชเสริมผ่าน
ความหมายการค้นหา/รวบรวมผลลัพธ์
หัวข้อที่มีชื่อว่า “ความหมายการค้นหา/รวบรวมผลลัพธ์”- Regex engine:
grep_regex::RegexMatcherBuilderพร้อมignoreCaseและmultiline - การกำหนด context:
contextBefore/contextAfterแทนที่ค่าcontextเดิม- โหมดที่ไม่ใช่ content จะเซ็ตการรวบรวม context เป็นศูนย์
- โหมดเอาต์พุต:
content=> หนึ่งGrepMatchต่อการพบcountและfilesWithMatchesทั้งสองแมปไปยัง count-style entries (lineNumber=0,line="",matchCountถูกตั้งค่า)
- ขีดจำกัด:
offsetและmaxCountแบบ global ใช้กับทุกไฟล์- เส้นทางขนานใช้เฉพาะเมื่อ
maxCountไม่ได้ตั้งค่าและoffset == 0มิฉะนั้นเส้นทางลำดับจะรักษาความหมาย global offset/limit แบบ deterministic
การจัดรูปแบบผลลัพธ์กลับสู่ JS
หัวข้อที่มีชื่อว่า “การจัดรูปแบบผลลัพธ์กลับสู่ JS”- ฟิลด์
SearchResult/GrepResultของ Rust แมปไปยัง TS types ผ่านการแปลงฟิลด์ N-API object - ตัวนับถูกจำกัดที่
u32ก่อนข้าม N-API - Boolean เสริมจะถูกละเว้นหากไม่เป็น true ในบางเส้นทาง (
limitReached) - Streaming callback รับแต่ละ
GrepMatchที่จัดรูปแบบแล้ว (content หรือ count entry)
พฤติกรรมเมื่อเกิดข้อผิดพลาด
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดข้อผิดพลาด”searchContentคืนค่าSearchResult.errorสำหรับข้อผิดพลาด regex/การค้นหาแทนการ throwgrepปฏิเสธด้วยข้อผิดพลาดร้ายแรง (path ไม่ถูกต้อง, glob/regex ไม่ถูกต้อง, การยกเลิก timeout/abort)hasMatchคืนค่าResult<bool>และ throw เมื่อ pattern/UTF-8 decoding errors ไม่ถูกต้อง- ข้อผิดพลาดการเปิดไฟล์/การค้นหาในการสแกนหลายไฟล์จะถูกข้ามต่อไฟล์ การสแกนดำเนินต่อไป
การจัดการ regex ที่ผิดรูปแบบ
หัวข้อที่มีชื่อว่า “การจัดการ regex ที่ผิดรูปแบบ”grep.rs ทำความสะอาดวงเล็บปีกกาก่อน compile regex:
- วงเล็บปีกกาที่คล้ายการทำซ้ำที่ไม่ถูกต้องจะถูก escape (
{/}->\{/\}) เมื่อไม่สามารถสร้าง{N},{N,},{N,M}ได้ - ป้องกันไม่ให้ template fragments ทั่วไป (เช่น
${platform}) ล้มเหลวเป็น malformed repetition - ไวยากรณ์ regex ที่ไม่ถูกต้องที่เหลืออยู่ยังคงคืนค่า regex error
2) การค้นพบไฟล์ (glob) และการค้นหา path แบบ fuzzy (fuzzyFind)
หัวข้อที่มีชื่อว่า “2) การค้นพบไฟล์ (glob) และการค้นหา path แบบ fuzzy (fuzzyFind)”glob และ fuzzyFind แชร์การสแกน fs_cache ร่วมกัน แต่ logic การจับคู่แตกต่างกัน
กระบวนการ glob
หัวข้อที่มีชื่อว่า “กระบวนการ glob”- TS wrapper (
glob/index.ts):path.resolve(options.path)- ค่าเริ่มต้น:
pattern="*",hidden=false,gitignore=true,recursive=true
- Rust
globสร้างGlobConfigและ compile pattern ผ่านglob_util::compile_glob - แหล่ง entry:
cache=true=>get_or_scan+force_rescanเสริมเมื่อผลลัพธ์ว่างล้าสมัยcache=false=>force_rescan(..., store=false)(ใหม่เท่านั้น)
- การกรอง:
- ข้าม
.gitเสมอ - ข้าม
node_modulesยกเว้นเมื่อร้องขอ (includeNodeModulesหรือ pattern ที่กล่าวถึง node_modules) - ใช้การจับคู่ glob
- ใช้ตัวกรองประเภทไฟล์ การกรอง symlink
file/dirจะแก้ไข target metadata
- ข้าม
- เรียงลำดับเสริมตาม mtime จากมากไปน้อย (
sortByMtime) ก่อนตัดเหลือmaxResults
กระบวนการ fuzzyFind (นำไปใช้ใน fd.rs)
หัวข้อที่มีชื่อว่า “กระบวนการ fuzzyFind (นำไปใช้ใน fd.rs)”- TS wrapper ส่งออกจาก module
grepแต่การนำไปใช้งาน Rust อยู่ในfd.rs - แหล่งการสแกนที่แชร์จาก
fs_cacheพร้อมการแยก cache/no-cache และนโยบาย stale-empty recheck เดียวกัน - การให้คะแนน:
- exact / starts-with / contains / subsequence-based fuzzy score
- เส้นทางการให้คะแนนที่ normalize separator/punctuation
- directory bonus และ tie-break แบบ deterministic (
score desc, แล้วpath asc)
- Symlink entries ถูกยกเว้นจากผลลัพธ์ fuzzy
พฤติกรรมเมื่อเกิดข้อผิดพลาด
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดข้อผิดพลาด”- glob pattern ไม่ถูกต้อง => error จาก
glob_util::compile_glob - Search root ต้องเป็นไดเรกทอรีที่มีอยู่ (
resolve_search_path) มิฉะนั้น error - การยกเลิก/timeout ส่งต่อเป็น abort errors ผ่านการตรวจสอบ
CancelToken::heartbeat()ในลูป
การจัดการ glob ที่ผิดรูปแบบ
หัวข้อที่มีชื่อว่า “การจัดการ glob ที่ผิดรูปแบบ”glob_util::build_glob_pattern มีความยืดหยุ่น:
- Normalize
\เป็น/ - เพิ่ม
**/นำหน้าโดยอัตโนมัติสำหรับ simple recursive patterns เมื่อrecursive=true - ปิดกลุ่ม alternation
{...ที่ไม่สมดุลโดยอัตโนมัติก่อน compile
3) วงจรชีวิตการสแกน/แคชที่แชร์ (fs_cache)
หัวข้อที่มีชื่อว่า “3) วงจรชีวิตการสแกน/แคชที่แชร์ (fs_cache)”fs_cache เก็บผลลัพธ์การสแกนเป็น entries สัมพัทธ์ที่ normalize (path, fileType, mtime เสริม) ที่ key โดย:
- search root แบบ canonical
include_hiddenuse_gitignore
การเปลี่ยนสถานะแคช
หัวข้อที่มีชื่อว่า “การเปลี่ยนสถานะแคช”- Miss / ปิดใช้งาน
- TTL เป็น
0หรือ key ไม่มี/หมดอายุ ->collect_entriesใหม่
- TTL เป็น
- Hit
- อายุ entry
< cache_ttl_ms()-> คืนค่า entries ที่แคชไว้ +cache_age_ms
- อายุ entry
- Stale-empty recheck (นโยบายผู้เรียกใน
glob/grep/fd)- หาก query ให้ผลการจับคู่เป็นศูนย์และ
cache_age_ms >= empty_recheck_ms()จะ force rescan หนึ่งครั้ง
- หาก query ให้ผลการจับคู่เป็นศูนย์และ
- การยกเลิก
invalidateFsScanCache(path?):- ไม่มี argument: ลบ keys ทั้งหมด
- มี path argument: ลบ keys ที่ root เป็น prefix ของ target path นั้น
การแลกเปลี่ยนผลลัพธ์ล้าสมัย
หัวข้อที่มีชื่อว่า “การแลกเปลี่ยนผลลัพธ์ล้าสมัย”- แคชให้ความสำคัญกับการสแกนซ้ำที่มี latency ต่ำมากกว่าความสอดคล้องทันที
- ช่วง TTL อาจคืนค่า positives/negatives ที่ล้าสมัย
- Empty-result recheck ลด stale negatives สำหรับการสแกนที่แคชไว้เก่ากว่าโดยแลกกับการสแกนเพิ่มเติมหนึ่งครั้ง
- การยกเลิกที่ชัดเจนคือ hook ความถูกต้องที่ตั้งใจไว้หลังจากการเปลี่ยนแปลงไฟล์
4) ยูทิลิตี้ข้อความ ANSI (text)
หัวข้อที่มีชื่อว่า “4) ยูทิลิตี้ข้อความ ANSI (text)”ยูทิลิตี้เหล่านี้เป็น pure, in-memory utilities (ไม่มีการสแกนระบบไฟล์)
ขอบเขตและความรับผิดชอบ
หัวข้อที่มีชื่อว่า “ขอบเขตและความรับผิดชอบ”text.rsเป็นเจ้าของ terminal-cell semantics:- การแยกวิเคราะห์ ANSI sequence
- ความกว้างและการตัด slice ที่รับรู้ grapheme
- พฤติกรรม wrap/truncate/sanitize
- การตัด line ใน
grep.rs(maxColumns) แยกต่างหาก:- การตัด matched lines ที่ character-boundary อย่างง่ายพร้อม
... - ไม่รักษา ANSI-state และไม่รับรู้ความกว้าง terminal-cell
- การตัด matched lines ที่ character-boundary อย่างง่ายพร้อม
พฤติกรรมหลัก
หัวข้อที่มีชื่อว่า “พฤติกรรมหลัก”wrapTextWithAnsi: ตัดบรรทัดตามความกว้างที่มองเห็นได้ ส่ง active SGR codes ข้ามบรรทัดที่ตัดแล้วtruncateToWidth: การตัด visible-cell พร้อมนโยบาย ellipsis (Unicode,Ascii,Omit), padding ด้านขวาเสริม และ fast-path ที่คืนค่า JS string เดิมเมื่อไม่เปลี่ยนแปลงsliceWithWidth: การตัด column slice พร้อมการบังคับความกว้างแบบ strict เสริมextractSegments: แยก before/after segments รอบ overlay ขณะคืนค่า ANSI state สำหรับ segmentaftersanitizeText: ลบ ANSI escapes + control chars, ทิ้ง lone surrogates, normalize CR/LF โดยลบ\rvisibleWidth: นับ visible terminal cells (tabs ใช้TAB_WIDTHคงที่จากการนำไปใช้ Rust)
พฤติกรรมเมื่อเกิดข้อผิดพลาด
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดข้อผิดพลาด”ฟังก์ชัน Text โดยทั่วไปคืนค่าเอาต์พุตที่แปลงแบบ deterministic ข้อผิดพลาดจำกัดอยู่ที่ขอบเขตการแปลง JS string (ความล้มเหลวการแปลง argument ของ N-API)
5) การ highlight syntax (highlight)
หัวข้อที่มีชื่อว่า “5) การ highlight syntax (highlight)”highlight.rs เป็น pure transformation (ไม่มี FS ไม่มีแคช)
กระบวนการ
หัวข้อที่มีชื่อว่า “กระบวนการ”- Wrapper ส่ง
code,langเสริม และ ANSI color palette - Rust แก้ไข syntax โดย:
- ค้นหา token/ชื่อ
- ค้นหา extension
- fallback ของ alias table (
ts/tsx/js -> JavaScriptเป็นต้น) - fallback เป็น plain text syntax เมื่อไม่สามารถแก้ไขได้
- แยกวิเคราะห์แต่ละบรรทัดด้วย syntect
ParseStateและ scope stack - แมป scopes ไปยัง 11 หมวดหมู่สี semantic และ inject/reset ANSI color codes
พฤติกรรมเมื่อเกิดข้อผิดพลาด
หัวข้อที่มีชื่อว่า “พฤติกรรมเมื่อเกิดข้อผิดพลาด”- ข้อผิดพลาดการแยกวิเคราะห์ต่อบรรทัดไม่ทำให้การเรียกล้มเหลว: บรรทัดนั้นถูกเพิ่มโดยไม่ highlight และการประมวลผลดำเนินต่อไป
- ภาษาที่ไม่รู้จัก/ไม่รองรับจะ fallback เป็น plain text syntax
Pure utility เทียบกับ flows ที่ขึ้นอยู่กับระบบไฟล์
หัวข้อที่มีชื่อว่า “Pure utility เทียบกับ flows ที่ขึ้นอยู่กับระบบไฟล์”| Flow | การเข้าถึงระบบไฟล์ | แคชที่แชร์ | หมายเหตุ |
|---|---|---|---|
searchContent / hasMatch | ไม่มี | ไม่มี | regex บน bytes/string ที่ให้มาเท่านั้น |
ฟังก์ชัน module text | ไม่มี | ไม่มี | ANSI/width/sanitization เท่านั้น |
ฟังก์ชัน module highlight | ไม่มี | ไม่มี | syntax + ANSI coloring เท่านั้น |
glob | มี | เสริม | directory scans + glob filtering |
fuzzyFind | มี | เสริม | directory scans + fuzzy scoring |
grep (file/dir path) | มี | เสริม (dir mode) | ripgrep เหนือไฟล์, filters/callback เสริม |
สรุปวงจรชีวิต end-to-end
หัวข้อที่มีชื่อว่า “สรุปวงจรชีวิต end-to-end”- Caller เรียก TS wrapper พร้อม typed options
- Wrapper normalize ค่าเริ่มต้น (โดยเฉพาะ
glob) และส่งต่อไปยัง exportnative.* - Rust ตรวจสอบ/normalize options และสร้าง matcher/search config
- สำหรับ filesystem flows entries จะถูกสแกน (cache hit/miss/rescan) จากนั้นกรอง/ให้คะแนน
- Worker loops เรียก cancel heartbeat เป็นระยะ timeout/abort สามารถยุติการประมวลผล
- Rust จัดรูปแบบเอาต์พุตเป็น N-API objects (
lineNumber,matchCount,limitReachedเป็นต้น) - TS wrapper คืนค่า typed JS objects (และ per-match callbacks เสริมสำหรับ
grep/glob)