Skip to content

Commit 42542c5

Browse files
committed
Move initial file analysis to the background
This ensures that the LSP server will respond to requests while the background parsing is going on.
1 parent c9bef29 commit 42542c5

File tree

2 files changed

+72
-55
lines changed

2 files changed

+72
-55
lines changed

server/src/analyser.ts

Lines changed: 64 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -36,63 +36,20 @@ export default class Analyzer {
3636
* anywhere on that path. This non-exhaustive glob is used to preload the parser
3737
* to support features across files.
3838
*/
39-
public static async fromRoot({
39+
public static fromRoot({
4040
connection,
4141
rootPath,
4242
parser,
4343
}: {
4444
connection: LSP.Connection
4545
rootPath: LSP.InitializeParams['rootPath']
4646
parser: Parser
47-
}): Promise<Analyzer> {
47+
}): Analyzer {
4848
const analyzer = new Analyzer(parser)
4949

5050
if (rootPath) {
51-
const globPattern = getGlobPattern()
52-
connection.console.log(
53-
`Analyzing files matching glob "${globPattern}" inside ${rootPath}`,
54-
)
55-
56-
const lookupStartTime = Date.now()
57-
const getTimePassed = (): string =>
58-
`${(Date.now() - lookupStartTime) / 1000} seconds`
59-
60-
let filePaths: string[] = []
61-
try {
62-
filePaths = await getFilePaths({ globPattern, rootPath })
63-
} catch (error) {
64-
const errorMessage = error instanceof Error ? error.message : error
65-
connection.window.showWarningMessage(
66-
`Failed to analyze bash files using the glob "${globPattern}". The experience will be degraded. Error: ${errorMessage}`,
67-
)
68-
}
69-
70-
// TODO: we could load all files without extensions: globPattern: '**/[^.]'
71-
72-
connection.console.log(
73-
`Glob resolved with ${filePaths.length} files after ${getTimePassed()}`,
74-
)
75-
76-
for (const filePath of filePaths) {
77-
const uri = url.pathToFileURL(filePath).href
78-
connection.console.log(`Analyzing ${uri}`)
79-
80-
try {
81-
const fileContent = await readFileAsync(filePath, 'utf8')
82-
const shebang = getShebang(fileContent)
83-
if (shebang && !isBashShebang(shebang)) {
84-
connection.console.log(`Skipping file ${uri} with shebang "${shebang}"`)
85-
continue
86-
}
87-
88-
analyzer.analyze(uri, LSP.TextDocument.create(uri, 'shell', 1, fileContent))
89-
} catch (error) {
90-
const errorMessage = error instanceof Error ? error.message : error
91-
connection.console.warn(`Failed analyzing ${uri}. Error: ${errorMessage}`)
92-
}
93-
}
94-
95-
connection.console.log(`Analyzer finished after ${getTimePassed()}`)
51+
// NOTE: we do not block the initialization on this background analysis.
52+
analyzer.initiateBackgroundAnalysis({ connection, rootPath })
9653
}
9754

9855
return analyzer
@@ -120,6 +77,66 @@ export default class Analyzer {
12077
this.parser = parser
12178
}
12279

80+
private async initiateBackgroundAnalysis({
81+
connection,
82+
rootPath,
83+
}: {
84+
connection: LSP.Connection
85+
rootPath: string
86+
}) {
87+
const globPattern = getGlobPattern()
88+
connection.console.log(
89+
`BackgroundAnalysis: resolving glob "${globPattern}" inside "${rootPath}"...`,
90+
)
91+
92+
const lookupStartTime = Date.now()
93+
const getTimePassed = (): string => `${(Date.now() - lookupStartTime) / 1000} seconds`
94+
95+
let filePaths: string[] = []
96+
try {
97+
filePaths = await getFilePaths({
98+
globPattern,
99+
rootPath,
100+
})
101+
} catch (error) {
102+
const errorMessage = error instanceof Error ? error.message : error
103+
connection.window.showWarningMessage(
104+
`BackgroundAnalysis: failed resolved glob "${globPattern}". The experience across files will be degraded. Error: ${errorMessage}`,
105+
)
106+
return
107+
}
108+
109+
connection.console.log(
110+
`BackgroundAnalysis: Glob resolved with ${
111+
filePaths.length
112+
} files after ${getTimePassed()}`,
113+
)
114+
115+
for (const filePath of filePaths) {
116+
const uri = url.pathToFileURL(filePath).href
117+
118+
try {
119+
const fileContent = await readFileAsync(filePath, 'utf8')
120+
const shebang = getShebang(fileContent)
121+
if (shebang && !isBashShebang(shebang)) {
122+
connection.console.log(
123+
`BackgroundAnalysis: Skipping file ${uri} with shebang "${shebang}"`,
124+
)
125+
continue
126+
}
127+
128+
this.analyze(uri, LSP.TextDocument.create(uri, 'shell', 1, fileContent))
129+
} catch (error) {
130+
const errorMessage = error instanceof Error ? error.message : error
131+
connection.console.warn(
132+
`BackgroundAnalysis: Failed analyzing ${uri}. Error: ${errorMessage}`,
133+
)
134+
}
135+
}
136+
137+
connection.console.log(`BackgroundAnalysis: Completed after ${getTimePassed()}.`)
138+
}
139+
123140
/**
124141
* Find all the locations where something named name has been defined.
125142
*/

server/src/server.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ export default class BashServer {
3838
throw new Error('Expected PATH environment variable to be set')
3939
}
4040

41-
return Promise.all([
42-
Executables.fromPath(PATH),
43-
Analyzer.fromRoot({ connection, rootPath, parser }),
44-
getLinterExecutablePath(),
45-
]).then(([executables, analyzer, linterExecutablePath]) => {
46-
const linter = new Linter({ executablePath: linterExecutablePath })
47-
return new BashServer(connection, executables, analyzer, linter, capabilities)
48-
})
41+
const analyzer = Analyzer.fromRoot({ connection, rootPath, parser })
42+
43+
return Promise.all([Executables.fromPath(PATH), getLinterExecutablePath()]).then(
44+
([executables, linterExecutablePath]) => {
45+
const linter = new Linter({ executablePath: linterExecutablePath })
46+
return new BashServer(connection, executables, analyzer, linter, capabilities)
47+
},
48+
)
4949
}
5050

5151
private executables: Executables

0 commit comments

Comments
 (0)