11import childProcess , { ChildProcess } from "child_process" ;
22import { join } from "path" ;
3- import { from , Subject , timer } from "rxjs" ;
3+ import { from , of , Subject , timer } from "rxjs" ;
44import { waitMap } from "rxjs-operators/lib/waitMap" ;
5- import { filter , map , switchMap } from "rxjs/operators" ;
5+ import { catchError , filter , map , switchMap , timeout } from "rxjs/operators" ;
66import { TextDocument } from "vscode-languageserver-textdocument" ;
77import { URI } from "vscode-uri" ;
88
99import logger from "../common/logger" ;
1010import { IParserHandles } from "../common/types" ;
11- import { handleParse } from "../common/util" ;
11+ import { delay } from "../common/util" ;
1212import { handleDiagnostic } from "../handles/diagnostic" ;
13+ import { INode } from "../lib/vimparser" ;
1314import config from "./config" ;
1415import { workspace } from "./workspaces" ;
1516
@@ -18,30 +19,36 @@ const log = logger("parser");
1819const parserHandles : IParserHandles = { } ;
1920
2021const indexes : Record < string , boolean > = { } ;
22+ const indexesFsPaths : Record < string , boolean > = { } ;
2123
2224const origin$ : Subject < TextDocument > = new Subject < TextDocument > ( ) ;
2325
26+ const parserCallbacks : Record < string , ( param : any ) => void > = { }
27+ const queueFsPaths : string [ ] = [ ]
28+
2429let scanProcess : ChildProcess ;
2530let isScanRuntimepath : boolean = false ;
31+ let isParsing = false
2632
27- function send ( params : any ) {
33+ function send ( params : any ) : boolean {
2834 if ( ! scanProcess ) {
2935 log . log ( 'scan process do not exists' )
30- return
36+ return false
3137 }
3238 if ( ( scanProcess as any ) . signalCode ) {
3339 log . log ( `scan process signal code: ${ ( scanProcess as any ) . signalCode } ` )
34- return
40+ return false
3541 }
3642 if ( scanProcess . killed ) {
3743 log . log ( 'scan process was killed' )
38- return
44+ return false
3945 }
4046 scanProcess . send ( params , ( err ) => {
4147 if ( err ) {
4248 log . warn ( `Send error: ${ err . stack || err . message || err . name } ` )
4349 }
4450 } )
51+ return true
4552}
4653
4754function startIndex ( ) {
@@ -54,16 +61,23 @@ function startIndex() {
5461 ) ;
5562
5663 scanProcess . on ( "message" , ( mess ) => {
57- const { data, msglog } = mess ;
58- if ( data ) {
59- if ( ! workspace . isExistsBuffer ( data . uri ) ) {
60- workspace . updateBuffer ( data . uri , data . node ) ;
61- }
62- }
64+ const { msglog, id, res, error, fsPaths } = mess ;
6365
6466 if ( msglog ) {
6567 log . info ( `child_log: ${ msglog } ` ) ;
6668 }
69+
70+ if ( fsPaths ) {
71+ parserFiles ( fsPaths )
72+ }
73+
74+ if ( id && parserCallbacks [ id ] ) {
75+ parserCallbacks [ id ] ( {
76+ res,
77+ error
78+ } )
79+ delete parserCallbacks [ id ]
80+ }
6781 } ) ;
6882
6983 scanProcess . on ( "error" , ( err : Error ) => {
@@ -108,16 +122,47 @@ export function next(
108122 ) ;
109123 } ) ,
110124 waitMap ( ( td : TextDocument ) => {
111- return from ( handleParse ( td ) ) ;
125+ const id = `${ Date . now ( ) } -${ Math . random ( ) } `
126+ return from ( new Promise < { res : any , error : any , isTimeout ?: boolean } > ( ( res ) => {
127+ parserCallbacks [ id ] = res
128+ send ( {
129+ id,
130+ uri,
131+ text : td . getText ( )
132+ } )
133+ } ) ) . pipe (
134+ timeout ( 10000 ) ,
135+ catchError ( ( ) => {
136+ if ( parserCallbacks [ id ] ) {
137+ delete parserCallbacks [ id ]
138+ }
139+ scanProcess . kill ( )
140+ scanProcess = undefined
141+ return of ( {
142+ res : '' ,
143+ error : `Timeout: 10000ms` ,
144+ isTimeout : true ,
145+ } )
146+ } )
147+ )
112148 } , true ) ,
113149 ) . subscribe (
114- ( res ) => {
115- if ( config . diagnostic . enable ) {
116- // handle diagnostic
117- handleDiagnostic ( textDoc , res [ 1 ] ) ;
150+ ( data ) => {
151+ const { res, error, isTimeout } = data
152+ if ( res ) {
153+ if ( config . diagnostic . enable ) {
154+ // handle diagnostic
155+ handleDiagnostic ( textDoc , res [ 1 ] ) ;
156+ }
157+ // handle node
158+ workspace . updateBuffer ( uri , res [ 0 ] ) ;
159+ }
160+ if ( error ) {
161+ log . error ( `Parse ${ uri } error: ${ error } ` )
162+ }
163+ if ( isTimeout ) {
164+ log . showErrorMessage ( `Parse ${ uri } error: ${ error } ` )
118165 }
119- // handle node
120- workspace . updateBuffer ( uri , res [ 0 ] ) ;
121166 // scan project
122167 if ( ! indexes [ uri ] ) {
123168 indexes [ uri ] = true ;
@@ -126,7 +171,7 @@ export function next(
126171 } )
127172 if ( ! isScanRuntimepath ) {
128173 isScanRuntimepath = true ;
129- scan ( [ config . vimruntime ] . concat ( config . runtimepath ) ) ;
174+ scanRuntimePaths ( [ config . vimruntime ] . concat ( config . runtimepath ) ) ;
130175 }
131176 }
132177 } ,
@@ -149,7 +194,7 @@ export function unsubscribe(textDoc: TextDocument) {
149194}
150195
151196// scan directory
152- export function scan ( paths : string | string [ ] ) {
197+ export function scanRuntimePaths ( paths : string | string [ ] ) {
153198 if ( ! scanProcess ) {
154199 startIndex ( ) ;
155200 }
@@ -164,9 +209,72 @@ export function scan(paths: string | string[]) {
164209 if ( ! p || p === "/" ) {
165210 continue ;
166211 }
167- send ( {
168- uri : URI . file ( join ( p , "f" ) ) . toString ( ) ,
169- } )
212+ const uri = URI . file ( join ( p , "f" ) ) . toString ( )
213+ if ( ! indexes [ uri ] ) {
214+ indexes [ uri ] = true ;
215+ send ( {
216+ uri
217+ } )
218+ }
170219 }
171220 }
172221}
222+
223+ export async function parserFiles ( paths : string [ ] ) {
224+ queueFsPaths . push ( ...paths )
225+ if ( isParsing ) {
226+ return
227+ }
228+ isParsing = true
229+ while ( queueFsPaths . length ) {
230+ await Promise . all (
231+ Array ( config . indexes . count ) . fill ( '' ) . map ( async ( ) => {
232+ const fsPath = queueFsPaths . shift ( )
233+ if ( ! fsPath || indexesFsPaths [ fsPath ] ) {
234+ return
235+ }
236+ indexesFsPaths [ fsPath ] = true
237+ const id = `${ Date . now ( ) } -${ Math . random ( ) } `
238+ const data = await new Promise < { res ?: [ INode | null , string ] , error : any , timeout ?: boolean } > ( res => {
239+ parserCallbacks [ id ] = res
240+ const isSend = send ( {
241+ id,
242+ fsPath
243+ } )
244+ if ( isSend ) {
245+ setTimeout ( ( ) => {
246+ delete parserCallbacks [ id ]
247+ res ( {
248+ error : 'Timeout 10000ms' ,
249+ timeout : true
250+ } )
251+ } , 10000 ) ;
252+ } else {
253+ queueFsPaths . unshift ( fsPath )
254+ delete parserCallbacks [ id ]
255+ res ( {
256+ error : `Cancel parser since scan process does not exists`
257+ } )
258+ }
259+ } )
260+ if ( data . res && data . res [ 0 ] ) {
261+ const uri = URI . file ( fsPath ) . toString ( )
262+ if ( ! workspace . isExistsBuffer ( uri ) ) {
263+ workspace . updateBuffer ( uri , data . res [ 0 ] ) ;
264+ }
265+ }
266+ if ( data . error ) {
267+ log . error ( `Parse ${ fsPath } error: ${ data . error } ` )
268+ }
269+ if ( data . timeout ) {
270+ scanProcess . kill ( )
271+ scanProcess = undefined
272+ startIndex ( )
273+ log . showErrorMessage ( `Parse ${ fsPath } error: ${ data . error } ` )
274+ }
275+ } )
276+ )
277+ await delay ( config . indexes . gap )
278+ }
279+ isParsing = false
280+ }
0 commit comments