Skip to content

Commit

Permalink
Refactor: Remove connection from class members etc
Browse files Browse the repository at this point in the history
OutputParser and serverNotificationManager don't need to instantiate
with connection. The connection can be set on start up.
Use Analyzer's uriToAnalyzedDocument for returning document texts to replace documentAsTextMap
onDefinitionHandler is in its own file for better testing
  • Loading branch information
WilsonZiweiWang committed Oct 26, 2023
1 parent 6f63aca commit 24d9d28
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 70 deletions.
32 changes: 12 additions & 20 deletions server/src/BitBakeProjectScanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,16 @@ import fs from 'fs'

import logger from 'winston'

import type {
Connection
} from 'vscode-languageserver'
import type {
ElementInfo,
LayerInfo,
PathInfo
} from './ElementInfo'

import {
OutputParser
} from './OutputParser'
import { ServerNotificationManager } from './ServerNotificationManager'

import
outputParser
from './OutputParser'
import { serverNotificationManager } from './ServerNotificationManager'
interface ScannStatus {
scanIsRunning: boolean
scanIsPending: boolean
Expand All @@ -42,18 +38,11 @@ export class BitBakeProjectScanner {
private _classes: ElementInfo[] = new Array < ElementInfo >()
private _includes: ElementInfo[] = new Array < ElementInfo >()
private _recipes: ElementInfo[] = new Array < ElementInfo >()
private readonly _outputParser: OutputParser
private readonly _notificationManager: ServerNotificationManager
private _shouldDeepExamine: boolean = false
private _pathToBuildFolder: string = 'build'
private _pathToEnvScript: string = 'oe-init-build-env'
private _pathToBitbakeFolder: string = 'bitbake'

constructor (connection: Connection) {
this._outputParser = new OutputParser(connection)
this._notificationManager = new ServerNotificationManager(connection)
}

private readonly _scanStatus: ScannStatus = {
scanIsRunning: false,
scanIsPending: false
Expand Down Expand Up @@ -190,7 +179,7 @@ export class BitBakeProjectScanner {
} else {
const error = commandResult.stderr.toString()
logger.error(`can not scan available layers error: ${error}`)
this._outputParser.parse(error)
outputParser.parse(error)
}
}

Expand Down Expand Up @@ -268,9 +257,9 @@ export class BitBakeProjectScanner {

const commandResult = this.executeBitBakeCommand('bitbake -p')
const output = commandResult.output.toString()
this._outputParser.parse(output)
if (this._outputParser.errorsFound()) {
this._outputParser.reportProblems()
outputParser.parse(output)
if (outputParser.errorsFound()) {
outputParser.reportProblems()
parsingSuccess = false
} else {
if (commandResult.status !== 0) {
Expand Down Expand Up @@ -354,7 +343,7 @@ export class BitBakeProjectScanner {
if (output.includes('bitbake')) {
// Seems like Bitbake could not be found.
// Are we sure this is the actual error?
this._notificationManager.sendBitBakeNotFound()
serverNotificationManager.sendBitBakeNotFound()
throw new Error(output)
}
}
Expand Down Expand Up @@ -386,3 +375,6 @@ export class BitBakeProjectScanner {
return scriptFileBuffer.join('\n')
}
}

const bitBakeProjectScanner = new BitBakeProjectScanner()
export default bitBakeProjectScanner
4 changes: 4 additions & 0 deletions server/src/ContextHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import type {
} from './SymbolScanner'

import logger from 'winston'
import bitBakeProjectScanner from './BitBakeProjectScanner'

/**
* ContextHandler
Expand Down Expand Up @@ -170,3 +171,6 @@ export class ContextHandler {
return keyword
}
}

const contextHandler = new ContextHandler(bitBakeProjectScanner)
export default contextHandler
31 changes: 24 additions & 7 deletions server/src/OutputParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ import {
ProblemsContainer
} from './ProblemsContainer'

let _connection: Connection | null = null

/**
* Set the connection. Should be done at startup.
*/
export function setOutputParserConnection (connection: Connection): void {
_connection = connection
}

export class OutputParser {
_problems: ProblemsContainer[] = []
_connection: Connection

constructor (connection: Connection) {
this._connection = connection
}

parse (message: string): void {
this.clearAllProblemsAndReport()
Expand Down Expand Up @@ -59,20 +63,33 @@ export class OutputParser {
}

reportProblems (): void {
if (_connection === null) {
logger.warn('The LSP Connection is not set. Dropping messages')
return
}
const connection = _connection
logger.debug(`reportProblems: ${this._problems.length}`)
this._problems.forEach((container: ProblemsContainer) => {
logger.debug(`send Diagnostic ${container.toString()}`)
void this._connection.sendDiagnostics(container.getDignosticData())
void connection.sendDiagnostics(container.getDignosticData())
})
}

clearAllProblemsAndReport (): void {
if (_connection === null) {
logger.warn('The LSP Connection is not set. Dropping messages')
return
}
const connection = _connection
logger.debug(`clearAllProblems: ${this._problems.length}`)
this._problems.forEach((container: ProblemsContainer) => {
logger.debug(`send Diagnostic ${container.toString()}`)
void this._connection.sendDiagnostics(container.getClearedDiagnosticData())
void connection.sendDiagnostics(container.getClearedDiagnosticData())
})

this._problems = []
}
}

const outputParser = new OutputParser()
export default outputParser
26 changes: 18 additions & 8 deletions server/src/ServerNotificationManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,31 @@

import { type Connection } from 'vscode-languageserver'

export type NotificationType =
'custom/bitBakeNotFound'
let _connection: Connection | null = null

export class ServerNotificationManager {
private readonly _connection: Connection
/**
* Set the connection. Should be done at startup.
*/
export function setNotificationManagerConnection (connection: Connection): void {
_connection = connection
}

constructor (connection: Connection) {
this._connection = connection
}
export type NotificationType =
'custom/bitBakeNotFound'

class ServerNotificationManager {
send (type: NotificationType, message?: string): void {
void this._connection.sendNotification(type, message)
if (_connection === null) {
// eslint-disable-next-line no-console
console.warn('The LSP Connection is not set. Dropping messages')
return
}
void _connection.sendNotification(type, message)
}

sendBitBakeNotFound (): void {
this.send('custom/bitBakeNotFound')
}
}

export const serverNotificationManager = new ServerNotificationManager()
20 changes: 20 additions & 0 deletions server/src/connectionHandlers/onDefinition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* --------------------------------------------------------------------------------------------
* Copyright (c) 2023 Savoir-faire Linux. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */

import logger from 'winston'
import { type TextDocumentPositionParams, type Definition } from 'vscode-languageserver/node'
import { analyzer } from '../tree-sitter/analyzer'
import contextHandler from '../ContextHandler'

export function onDefinitionHandler (textDocumentPositionParams: TextDocumentPositionParams): Definition {
logger.debug(`onDefinition ${JSON.stringify(textDocumentPositionParams)}`)
const documentAsText = analyzer.getDocumentTexts(textDocumentPositionParams.textDocument.uri)

if (documentAsText === undefined) {
return []
}

return contextHandler.getDefinition(textDocumentPositionParams, documentAsText)
}
48 changes: 15 additions & 33 deletions server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
import {
type Connection,
type InitializeResult,
type TextDocumentPositionParams,
type CompletionItem,
type Definition,
type Hover,
createConnection,
TextDocuments,
Expand All @@ -17,26 +15,28 @@ import {
type InitializeParams
} from 'vscode-languageserver/node'
import { bitBakeDocScanner } from './BitBakeDocScanner'
import { BitBakeProjectScanner } from './BitBakeProjectScanner'
import { ContextHandler } from './ContextHandler'
import bitBakeProjectScanner from './BitBakeProjectScanner'
import contextHandler from './ContextHandler'
import { SymbolScanner } from './SymbolScanner'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { analyzer } from './tree-sitter/analyzer'
import { generateParser } from './tree-sitter/parser'
import logger from 'winston'
import { onCompletionHandler } from './connectionHandlers/onCompletion'

import { onDefinitionHandler } from './connectionHandlers/onDefinition'
import { setOutputParserConnection } from './OutputParser'
import { setNotificationManagerConnection } from './ServerNotificationManager'
// Create a connection for the server. The connection uses Node's IPC as a transport
const connection: Connection = createConnection(ProposedFeatures.all)
const documents = new TextDocuments<TextDocument>(TextDocument)
const documentAsTextMap = new Map<string, string[]>()
const bitBakeProjectScanner: BitBakeProjectScanner = new BitBakeProjectScanner(connection)
const contextHandler: ContextHandler = new ContextHandler(bitBakeProjectScanner)

connection.onInitialize(async (params: InitializeParams): Promise<InitializeResult> => {
const workspaceRoot = params.workspaceFolders?.[0]?.uri ?? ''
bitBakeProjectScanner.setProjectPath(workspaceRoot)

setOutputParserConnection(connection)
setNotificationManagerConnection(connection)

const parser = await generateParser()
analyzer.initialize(parser)

Expand Down Expand Up @@ -108,20 +108,11 @@ connection.onExecuteCommand((params) => {
}
})

connection.onDefinition((textDocumentPositionParams: TextDocumentPositionParams): Definition => {
logger.debug(`onDefinition ${JSON.stringify(textDocumentPositionParams)}`)
const documentAsText = documentAsTextMap.get(textDocumentPositionParams.textDocument.uri)

if (documentAsText === undefined) {
return []
}

return contextHandler.getDefinition(textDocumentPositionParams, documentAsText)
})
connection.onDefinition(onDefinitionHandler)

connection.onHover(async (params): Promise<Hover | undefined> => {
const { position, textDocument } = params
const documentAsText = documentAsTextMap.get(textDocument.uri)
const documentAsText = analyzer.getDocumentTexts(textDocument.uri)
const textLine = documentAsText?.[position.line]
if (textLine === undefined) {
return undefined
Expand Down Expand Up @@ -158,33 +149,24 @@ connection.onHover(async (params): Promise<Hover | undefined> => {

connection.listen()

documents.onDidOpen((event) => {
const textDocument = event.document
if (textDocument.getText().length > 0) {
documentAsTextMap.set(textDocument.uri, textDocument.getText().split(/\r?\n/g))
}

setSymbolScanner(new SymbolScanner(textDocument.uri, contextHandler.definitionProvider))
})

documents.onDidChangeContent(async (event) => {
const textDocument = event.document
documentAsTextMap.set(textDocument.uri, textDocument.getText().split(/\r?\n/g))

setSymbolScanner(new SymbolScanner(textDocument.uri, contextHandler.definitionProvider))

const diagnostics = await analyzer.analyze({ document: event.document, uri: event.document.uri })

void connection.sendDiagnostics({ uri: textDocument.uri, diagnostics })
if (textDocument.getText().length > 0) {
const diagnostics = await analyzer.analyze({ document: textDocument, uri: textDocument.uri })
void connection.sendDiagnostics({ uri: textDocument.uri, diagnostics })
}

// Other language extensions might also associate .conf files with their langauge modes
if (textDocument.uri.endsWith('.conf')) {
logger.debug('verifyConfigurationFileAssociation')
await connection.sendRequest('custom/verifyConfigurationFileAssociation', { filePath: new URL(textDocument.uri).pathname })
}
})

documents.onDidClose((event) => {
documentAsTextMap.delete(event.document.uri)
setSymbolScanner(null)
})

Expand Down
9 changes: 7 additions & 2 deletions server/src/tree-sitter/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export default class Analyzer {
private uriToAnalyzedDocument: Record<string, AnalyzedDocument | undefined> = {}
private debouncedExecuteAnalyzation?: ReturnType<typeof debounce>

public getDocumentTexts (uri: string): string[] | undefined {
return this.uriToAnalyzedDocument[uri]?.document.getText().split(/\r?\n/g)
}

public initialize (parser: Parser): void {
this.parser = parser
}
Expand All @@ -42,12 +46,13 @@ export default class Analyzer {
document: TextDocument
uri: string
}): Promise<Diagnostic[]> {
const fileContent = document.getText()

if (this.parser === undefined) {
console.log('The analyzer is not initialized with a parser')
return await Promise.resolve([])
}

const fileContent = document.getText()

const tree = this.parser.parse(fileContent)
const globalDeclarations = getGlobalDeclarations({ tree, uri })

Expand Down

0 comments on commit 24d9d28

Please sign in to comment.