Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: Remove connection from class members etc #36

Merged
merged 1 commit into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
WilsonZiweiWang marked this conversation as resolved.
Show resolved Hide resolved

/**
* 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) {
WilsonZiweiWang marked this conversation as resolved.
Show resolved Hide resolved
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)
WilsonZiweiWang marked this conversation as resolved.
Show resolved Hide resolved

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
Loading