2024-07-07 18:32:30 +03:00
import { svg } from '../svg.ts' ;
import { toggleElem } from '../utils/dom.ts' ;
import { pathEscapeSegments } from '../utils/url.ts' ;
import { GET } from '../modules/fetch.ts' ;
2022-06-09 14:15:08 +03:00
const threshold = 50 ;
let files = [ ] ;
2024-02-28 17:11:54 +03:00
let repoFindFileInput , repoFindFileTableBody , repoFindFileNoResult ;
2022-06-09 14:15:08 +03:00
2022-10-08 14:22:44 +03:00
// return the case-insensitive sub-match result as an array: [unmatched, matched, unmatched, matched, ...]
// res[even] is unmatched, res[odd] is matched, see unit tests for examples
// argument subLower must be a lower-cased string.
export function strSubMatch ( full , subLower ) {
const res = [ '' ] ;
let i = 0 , j = 0 ;
const fullLower = full . toLowerCase ( ) ;
while ( i < subLower . length && j < fullLower . length ) {
if ( subLower [ i ] === fullLower [ j ] ) {
if ( res . length % 2 !== 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full [ j ] ;
j ++ ;
i ++ ;
} else {
if ( res . length % 2 === 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full [ j ] ;
j ++ ;
}
}
if ( i !== subLower . length ) {
// if the sub string doesn't match the full, only return the full as unmatched.
return [ full ] ;
}
if ( j < full . length ) {
// append remaining chars from full to result as unmatched
if ( res . length % 2 === 0 ) res . push ( '' ) ;
res [ res . length - 1 ] += full . substring ( j ) ;
}
return res ;
}
export function calcMatchedWeight ( matchResult ) {
let weight = 0 ;
for ( let i = 0 ; i < matchResult . length ; i ++ ) {
if ( i % 2 === 1 ) { // matches are on odd indices, see strSubMatch
// use a function f(x+x) > f(x) + f(x) to make the longer matched string has higher weight.
weight += matchResult [ i ] . length * matchResult [ i ] . length ;
}
}
return weight ;
}
export function filterRepoFilesWeighted ( files , filter ) {
let filterResult = [ ] ;
2022-06-09 14:15:08 +03:00
if ( filter ) {
2022-10-08 14:22:44 +03:00
const filterLower = filter . toLowerCase ( ) ;
// TODO: for large repo, this loop could be slow, maybe there could be one more limit:
// ... && filterResult.length < threshold * 20, wait for more feedbacks
2024-07-03 18:48:14 +03:00
for ( const file of files ) {
const res = strSubMatch ( file , filterLower ) ;
2022-10-08 14:22:44 +03:00
if ( res . length > 1 ) { // length==1 means unmatched, >1 means having matched sub strings
filterResult . push ( { matchResult : res , matchWeight : calcMatchedWeight ( res ) } ) ;
2022-06-09 14:15:08 +03:00
}
}
2022-10-08 14:22:44 +03:00
filterResult . sort ( ( a , b ) = > b . matchWeight - a . matchWeight ) ;
filterResult = filterResult . slice ( 0 , threshold ) ;
2022-06-09 14:15:08 +03:00
} else {
for ( let i = 0 ; i < files . length && i < threshold ; i ++ ) {
2022-10-08 14:22:44 +03:00
filterResult . push ( { matchResult : [ files [ i ] ] , matchWeight : 0 } ) ;
2022-06-09 14:15:08 +03:00
}
}
2022-10-08 14:22:44 +03:00
return filterResult ;
}
function filterRepoFiles ( filter ) {
2024-02-28 17:11:54 +03:00
const treeLink = repoFindFileInput . getAttribute ( 'data-url-tree-link' ) ;
repoFindFileTableBody . innerHTML = '' ;
2022-06-09 14:15:08 +03:00
2022-10-08 14:22:44 +03:00
const filterResult = filterRepoFilesWeighted ( files , filter ) ;
2022-06-09 14:15:08 +03:00
2024-03-25 21:37:55 +03:00
toggleElem ( repoFindFileNoResult , ! filterResult . length ) ;
2022-10-08 14:22:44 +03:00
for ( const r of filterResult ) {
2024-02-28 17:11:54 +03:00
const row = document . createElement ( 'tr' ) ;
const cell = document . createElement ( 'td' ) ;
const a = document . createElement ( 'a' ) ;
a . setAttribute ( 'href' , ` ${ treeLink } / ${ pathEscapeSegments ( r . matchResult . join ( '' ) ) } ` ) ;
Migrate margin and padding helpers to tailwind (#30043)
This will conclude the refactor of 1:1 class replacements to tailwind,
except `gt-hidden`. Commands ran:
```bash
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-0#tw-$1$2-0#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-1#tw-$1$2-0.5#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-2#tw-$1$2-1#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-3#tw-$1$2-2#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-4#tw-$1$2-4#g' {web_src/js,templates,routers,services}/**/*
perl -p -i -e 's#gt-(p|m)([lrtbxy])?-5#tw-$1$2-8#g' {web_src/js,templates,routers,services}/**/*
```
2024-03-24 19:42:49 +03:00
a . innerHTML = svg ( 'octicon-file' , 16 , 'tw-mr-2' ) ;
2024-02-28 17:11:54 +03:00
row . append ( cell ) ;
cell . append ( a ) ;
for ( const [ index , part ] of r . matchResult . entries ( ) ) {
const span = document . createElement ( 'span' ) ;
// safely escape by using textContent
span . textContent = part ;
2024-12-03 19:52:25 +03:00
span . title = span . textContent ;
2024-02-28 17:11:54 +03:00
// if the target file path is "abc/xyz", to search "bx", then the matchResult is ['a', 'b', 'c/', 'x', 'yz']
// the matchResult[odd] is matched and highlighted to red.
if ( index % 2 === 1 ) span . classList . add ( 'ui' , 'text' , 'red' ) ;
a . append ( span ) ;
2022-06-09 14:15:08 +03:00
}
2024-02-28 17:11:54 +03:00
repoFindFileTableBody . append ( row ) ;
2022-06-09 14:15:08 +03:00
}
}
async function loadRepoFiles() {
2024-02-28 17:11:54 +03:00
const response = await GET ( repoFindFileInput . getAttribute ( 'data-url-data-link' ) ) ;
files = await response . json ( ) ;
filterRepoFiles ( repoFindFileInput . value ) ;
2022-06-09 14:15:08 +03:00
}
export function initFindFileInRepo() {
2024-06-10 23:49:33 +03:00
repoFindFileInput = document . querySelector ( '#repo-file-find-input' ) ;
2024-02-28 17:11:54 +03:00
if ( ! repoFindFileInput ) return ;
2022-06-09 14:15:08 +03:00
2024-02-28 17:11:54 +03:00
repoFindFileTableBody = document . querySelector ( '#repo-find-file-table tbody' ) ;
2024-06-10 23:49:33 +03:00
repoFindFileNoResult = document . querySelector ( '#repo-find-file-no-result' ) ;
2024-02-28 17:11:54 +03:00
repoFindFileInput . addEventListener ( 'input' , ( ) = > filterRepoFiles ( repoFindFileInput . value ) ) ;
2022-06-09 14:15:08 +03:00
loadRepoFiles ( ) ;
}