@@ -7,30 +7,55 @@ const _ = require('lodash');
77
88const DECLARATION_TYPES = [ 'class' , 'module' , 'method' , 'classMethod' ] ;
99
10- function flatten ( locateInfo , file , parent ) {
10+ function flatten ( locateInfo , file , containerName = '' ) {
1111 return _ . flatMap ( locateInfo , ( symbols , type ) => {
1212 if ( ! _ . includes ( DECLARATION_TYPES , type ) ) {
1313 // Skip top-level include or posn property etc.
1414 return [ ] ;
1515 }
1616 return _ . flatMap ( symbols , ( inner , name ) => {
17- const sep = { method : '#' , classMethod : '.' } [ type ] || '::' ;
1817 const posn = inner . posn || { line : 0 , char : 0 } ;
19- const fullName = parent ? ` ${ parent . fullName } ${ sep } ${ name } ` : name ;
18+ // TODO: parse name with multiple segments, e.g. File.read or ActiveRecord::Base, if necessary.
2019 const symbolInfo = {
2120 name : name ,
2221 type : type ,
2322 file : file ,
2423 line : posn . line ,
2524 char : posn . char ,
26- parent : parent ,
27- fullName : fullName
25+ containerName : containerName || ''
2826 } ;
2927 _ . extend ( symbolInfo , _ . omit ( inner , DECLARATION_TYPES ) ) ;
30- return [ symbolInfo ] . concat ( flatten ( inner , file , symbolInfo ) ) ;
28+ const sep = { method : '#' , classMethod : '.' } [ type ] || '::' ;
29+ const fullName = containerName ? `${ containerName } ${ sep } ${ name } ` : name ;
30+ return [ symbolInfo ] . concat ( flatten ( inner , file , fullName ) ) ;
3131 } ) ;
3232 } ) ;
3333}
34+ function camelCaseRegExp ( query ) {
35+ const escaped = _ . escapeRegExp ( query )
36+ const prefix = escaped . charAt ( 0 ) ;
37+ return new RegExp (
38+ `[${ prefix . toLowerCase ( ) } ${ prefix . toUpperCase ( ) } ]` +
39+ escaped . slice ( 1 ) . replace ( / [ A - Z ] | ( [ a - z ] ) / g, ( char , lower ) => {
40+ if ( lower ) return `${ char . toUpperCase ( ) } ${ char } ` ;
41+ const lowered = char . toLowerCase ( )
42+ return `.*(?:${ char } |_${ lowered } )` ;
43+ } )
44+ ) ;
45+ }
46+ function filter ( symbols , query , stringProvider ) {
47+ // TODO: Ask MS to expose or separate matchesFuzzy method.
48+ // https://github.com/Microsoft/vscode/blob/a1d3c8a3006d0a3d68495122ea09a2a37bca69db/src/vs/base/common/filters.ts
49+ const isLowerCase = ( query . toLowerCase ( ) === query )
50+ const exact = new RegExp ( '^' + _ . escapeRegExp ( query ) + '$' , 'i' ) ;
51+ const prefix = new RegExp ( '^' + _ . escapeRegExp ( query ) , 'i' ) ;
52+ const substring = new RegExp ( _ . escapeRegExp ( query ) , isLowerCase ? 'i' : '' ) ;
53+ const camelCase = camelCaseRegExp ( query ) ;
54+ return _ ( [ exact , prefix , substring , camelCase ] )
55+ . flatMap ( regexp => symbols . filter ( symbolInfo => regexp . test ( stringProvider ( symbolInfo ) ) ) )
56+ . uniq ( )
57+ . value ( ) ;
58+ }
3459module . exports = class Locate {
3560 constructor ( root , settings ) {
3661 this . settings = settings ;
@@ -58,6 +83,18 @@ module.exports = class Locate {
5883 . map ( _ . clone )
5984 . value ( ) ;
6085 }
86+ query ( query ) {
87+ const match = query . match ( / ^ (?: ( .* ) [ . # ] ) ? ( [ ^ . # ] * ) $ / ) ;
88+ const containerQuery = match [ 1 ] ;
89+ const nameQuery = match [ 2 ] ;
90+ if ( ! nameQuery ) return [ ] ;
91+
92+ const symbols = _ ( this . tree ) . values ( ) . flatten ( ) . value ( ) ;
93+ const matchedSymbols = filter ( symbols , nameQuery , symbolInfo => symbolInfo . name ) ;
94+ if ( ! containerQuery ) return matchedSymbols ;
95+
96+ return filter ( matchedSymbols , containerQuery , symbolInfo => symbolInfo . containerName ) ;
97+ }
6198 rm ( absPath ) {
6299 if ( absPath in this . tree ) delete this . tree [ absPath ] ;
63100 }
0 commit comments