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

Implement codeLens #209

Draft
wants to merge 4 commits into
base: staging
Choose a base branch
from
Draft
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
17 changes: 17 additions & 0 deletions client/src/ui/BitbakeCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ export function registerBitbakeCommands (context: vscode.ExtensionContext, bitba
vscode.commands.registerCommand('bitbake.stop-toaster', async () => { await stopToaster(bitBakeProjectScanner.bitbakeDriver) }),
vscode.commands.registerCommand('bitbake.clear-workspace-state', async () => { await clearAllWorkspaceState(context) }),
vscode.commands.registerCommand('bitbake.examine-dependency-taskexp', async (uri) => { await examineDependenciesTaskexp(bitbakeWorkspace, bitBakeProjectScanner, uri) }),
vscode.commands.registerCommand('bitbake.codeLens.showReferences', async (uri, position) => { await showReferences(uri, position) }),

// Handles enqueued parsing requests (onSave)
vscode.tasks.onDidEndTask((e) => {
if (e.execution.task.name === 'Bitbake: Parse') {
Expand All @@ -70,6 +72,21 @@ export function registerBitbakeCommands (context: vscode.ExtensionContext, bitba
)
}

async function showReferences (uri: any, position: any): Promise<void> {
if (typeof uri !== 'string' || position === undefined) {
return
}

let _position: vscode.Position
try {
_position = new vscode.Position(position.line, position.character)
const locations = await vscode.commands.executeCommand('vscode.executeReferenceProvider', vscode.Uri.parse(uri), position)
await vscode.commands.executeCommand('editor.action.showReferences', vscode.Uri.parse(uri), _position, locations)
} catch (error: any) {
void vscode.window.showErrorMessage('Failed to show references: ' + JSON.stringify(error))
}
}

async function clearAllWorkspaceState (context: vscode.ExtensionContext, key?: string): Promise<void> {
for (const key of context.workspaceState.keys()) {
await context.workspaceState.update(key, undefined).then(
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@
"type": "boolean",
"default": false,
"description": "Turn on this setting to stop this extension from modifying the VS Code configuration of the workspace. Note that this will also disable some features related to embedded Python and Shell code."
},
"bitbake.enableCodeLensReferencesOnFunctions": {
"type": "boolean",
"default": false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not very familiar with CodeLens. So you're disabling it by default on purpose, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this surprised me also. However since currently we only have code lens for references, which have their dedicated button, I'm not sure the feature adds much value right now. Meanwhile it would bloat the interface.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This demo is cool however I think codelenses are justified when we have specific commands to run. There are few extensions doing so and the additional buttons could annoy users. A useful example for instance is the git extension which adds additional commands for comparing with other branches.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we would need different commands in order for this to be useful. I cannot think of one that isn't already seemlessly integrated into the interface yet (value hovers, rename, definitions, references...).

"description": "Enable or disable the CodeLens references for functions in BitBake files."
}
}
},
Expand Down
6 changes: 3 additions & 3 deletions server/src/__tests__/analyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('analyze', () => {
document: FIXTURE_DOCUMENT.DECLARATION
})

const globalDeclarations = analyzer.getGlobalDeclarationSymbols(DUMMY_URI)
const globalDeclarations = analyzer.getGlobalDeclarationSymbolsForUri(DUMMY_URI)

expect(globalDeclarations).toEqual(
expect.arrayContaining([
Expand Down Expand Up @@ -261,7 +261,7 @@ describe('sourceIncludeFiles', () => {
analyzer.extractIncludeFileUris(uri)

const symbols = analyzer.getIncludeUrisForUri(uri).map((includeUri) => {
return analyzer.getGlobalDeclarationSymbols(includeUri)
return analyzer.getGlobalDeclarationSymbolsForUri(includeUri)
}).flat()

expect(symbols).toEqual(
Expand Down Expand Up @@ -334,7 +334,7 @@ describe('declarations', () => {
uri
})

const symbols = analyzer.getGlobalDeclarationSymbols(uri)
const symbols = analyzer.getGlobalDeclarationSymbolsForUri(uri)

let occurances = 0
symbols.forEach((symbol) => {
Expand Down
38 changes: 38 additions & 0 deletions server/src/connectionHandlers/onCodeLens.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/* --------------------------------------------------------------------------------------------
* 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 * as LSP from 'vscode-languageserver/node'
import { analyzer } from '../tree-sitter/analyzer'

export async function onCodeLensHandler (params: LSP.CodeLensParams, enableCodeLensReferencesOnFunctions: boolean): Promise<LSP.CodeLens[]> {
const codeLenses: LSP.CodeLens[] = []
const uri = params.textDocument.uri

if (!enableCodeLensReferencesOnFunctions) {
return []
}

const allSymbols = analyzer.getGlobalDeclarationSymbolsForUri(uri)
allSymbols.forEach((symbol) => {
if (symbol.kind === LSP.SymbolKind.Function) {
const codeLens = LSP.CodeLens.create(symbol.location.range)

codeLens.command = {
title: 'Show References',
command: 'bitbake.codeLens.showReferences',
arguments: [uri, symbol.location.range.start]
}

codeLens.data = { uri, position: symbol.location.range.start }

codeLenses.push(codeLens)
}
})
return codeLenses
}

export function onCodeLensResolveHandler (codeLens: LSP.CodeLens): LSP.CodeLens {
return codeLens
}
4 changes: 2 additions & 2 deletions server/src/connectionHandlers/onCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ const allCommonDirectoriesCompletionItems: CompletionItem[] = Array.from(commonD
function getSymbolCompletionItems (word: string | null): CompletionItem[] {
if (word !== null) {
const uniqueSymbolSet = new Set()
const globalDeclarationSymbols = analyzer.getGlobalDeclarationSymbols(documentUri).filter(symbol => {
const globalDeclarationSymbols = analyzer.getGlobalDeclarationSymbolsForUri(documentUri).filter(symbol => {
if (!uniqueSymbolSet.has(symbol.name)) {
uniqueSymbolSet.add(symbol.name)
return true
Expand Down Expand Up @@ -397,7 +397,7 @@ function convertExtraSymbolsToCompletionItems (uri: string): CompletionItem[] {
logger.debug(`[onCompletion] convertSymbolsToCompletionItems: ${uri}`)
let completionItems: CompletionItem[] = []
analyzer.getIncludeUrisForUri(uri).map((includeUri) => {
return analyzer.getGlobalDeclarationSymbols(includeUri)
return analyzer.getGlobalDeclarationSymbolsForUri(includeUri)
})
.flat()
.reduce<BitbakeSymbolInformation[]>((acc, symbol) => {
Expand Down
4 changes: 2 additions & 2 deletions server/src/connectionHandlers/onDefinition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ export function getAllDefinitionSymbolsForSymbolAtPoint (uri: string, word: stri
return []
}
const allDeclarationSymbols = [
...analyzer.getGlobalDeclarationSymbols(uri)
...analyzer.getGlobalDeclarationSymbolsForUri(uri)
]
analyzer.getIncludeUrisForUri(uri)?.forEach((includeFileUri) => {
allDeclarationSymbols.push(...analyzer.getGlobalDeclarationSymbols(includeFileUri))
allDeclarationSymbols.push(...analyzer.getGlobalDeclarationSymbolsForUri(includeFileUri))
})

return allDeclarationSymbols.filter(symbol => symbol.name === word && symbol.kind === symbolAtPoint?.kind)
Expand Down
6 changes: 3 additions & 3 deletions server/src/connectionHandlers/onHover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export async function onHoverHandler (params: HoverParams): Promise<Hover | null

// Show documentation of a bitbake variable
// Triggers on global declaration expressions like "VAR = 'foo'" and inside variable expansion like "FOO = ${VAR}" but skip the ones like "python VAR(){}"
const canShowHoverDefinitionForVariableName: boolean = (analyzer.getGlobalDeclarationSymbols(textDocument.uri).some((symbol) => symbol.name === word) && analyzer.isIdentifierOfVariableAssignment(params)) || analyzer.isVariableExpansion(textDocument.uri, position.line, position.character) || analyzer.isPythonDatastoreVariable(textDocument.uri, position.line, position.character) || analyzer.isBashVariableExpansion(textDocument.uri, position.line, position.character)
const canShowHoverDefinitionForVariableName: boolean = (analyzer.getGlobalDeclarationSymbolsForUri(textDocument.uri).some((symbol) => symbol.name === word) && analyzer.isIdentifierOfVariableAssignment(params)) || analyzer.isVariableExpansion(textDocument.uri, position.line, position.character) || analyzer.isPythonDatastoreVariable(textDocument.uri, position.line, position.character) || analyzer.isBashVariableExpansion(textDocument.uri, position.line, position.character)
if (canShowHoverDefinitionForVariableName) {
const found = [
...bitBakeDocScanner.bitbakeVariableInfo.filter((bitbakeVariable) => !bitBakeDocScanner.yoctoVariableInfo.some(yoctoVariable => yoctoVariable.name === bitbakeVariable.name)),
Expand Down Expand Up @@ -121,11 +121,11 @@ export async function onHoverHandler (params: HoverParams): Promise<Hover | null
}

function getGlobalSymbolComments (uri: string, word: string, currentSymbolAtPoint: BitbakeSymbolInformation): string | null {
const localSymbolsWithComments = analyzer.getGlobalDeclarationSymbols(uri).filter((symbol) => symbol.name === word).filter((symbol) => symbol.commentsAbove.length > 0)
const localSymbolsWithComments = analyzer.getGlobalDeclarationSymbolsForUri(uri).filter((symbol) => symbol.name === word).filter((symbol) => symbol.commentsAbove.length > 0)
const externalSymbolsWithComments: BitbakeSymbolInformation[] = []

analyzer.getIncludeUrisForUri(uri).forEach((includeFileUri) => {
externalSymbolsWithComments.push(...analyzer.getGlobalDeclarationSymbols(includeFileUri).filter((symbol) => symbol.name === word).filter((symbol) => symbol.commentsAbove.length > 0))
externalSymbolsWithComments.push(...analyzer.getGlobalDeclarationSymbolsForUri(includeFileUri).filter((symbol) => symbol.name === word).filter((symbol) => symbol.commentsAbove.length > 0))
})
const priority = ['.bbclass', '.conf', '.inc', '.bb', '.bbappend']

Expand Down
13 changes: 12 additions & 1 deletion server/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,15 @@ import { expandSettingPath } from './lib/src/BitbakeSettings'
import { onReferenceHandler } from './connectionHandlers/onReference'
import { type BitbakeScanResult } from './lib/src/types/BitbakeScanResult'
import { onPrepareRenameHandler, onRenameRequestHandler } from './connectionHandlers/onRename'
import { onCodeLensHandler, onCodeLensResolveHandler } from './connectionHandlers/onCodeLens'

// Create a connection for the server. The connection uses Node's IPC as a transport
export const connection: Connection = createConnection(ProposedFeatures.all)
setDefinitionsConnection(connection)
const documents = new TextDocuments<TextDocument>(TextDocument)
let workspaceFolder: string | undefined
let pokyFolder: string | undefined

let enableCodeLensReferencesOnFunctions: boolean = false
const disposables: Disposable[] = []

let currentActiveTextDocument: TextDocument = TextDocument.create(
Expand Down Expand Up @@ -111,13 +112,17 @@ disposables.push(
},
renameProvider: {
prepareProvider: true
},
codeLensProvider: {
resolveProvider: true
}
}
}
}),

connection.onDidChangeConfiguration((change) => {
logger.level = change.settings.bitbake?.loggingLevel ?? logger.level
enableCodeLensReferencesOnFunctions = change.settings.bitbake?.enableCodeLensReferencesOnFunctions ?? enableCodeLensReferencesOnFunctions
const bitbakeFolder = expandSettingPath(change.settings.bitbake?.pathToBitbakeFolder, { workspaceFolder })
if (bitbakeFolder !== undefined) {
pokyFolder = path.join(bitbakeFolder, '..') // We assume BitBake is into Poky
Expand Down Expand Up @@ -156,6 +161,12 @@ disposables.push(
analyzer.clearRecipeLocalFiles()
}),

connection.onCodeLens(
async (params) => await onCodeLensHandler(params, enableCodeLensReferencesOnFunctions)
),

connection.onCodeLensResolve(onCodeLensResolveHandler),

connection.onRequest(
RequestMethod.EmbeddedLanguageTypeOnPosition,
async ({ uriString, position }: RequestParams['EmbeddedLanguageTypeOnPosition']): RequestResult['EmbeddedLanguageTypeOnPosition'] => {
Expand Down
4 changes: 2 additions & 2 deletions server/src/tree-sitter/analyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export default class Analyzer {

public getAllSymbols (uri: string): BitbakeSymbolInformation[] {
return [
...this.getGlobalDeclarationSymbols(uri),
...this.getGlobalDeclarationSymbolsForUri(uri),
...this.getVariableExpansionSymbols(uri),
...this.getPythonDatastoreVariableSymbols(uri)
]
Expand Down Expand Up @@ -185,7 +185,7 @@ export default class Analyzer {
return { variableExpansionSymbols, pythonDatastoreVariableSymbols }
}

public getGlobalDeclarationSymbols (uri: string): BitbakeSymbolInformation[] {
public getGlobalDeclarationSymbolsForUri (uri: string): BitbakeSymbolInformation[] {
const analyzedDocument = this.uriToAnalyzedDocument[uri]
if (analyzedDocument !== undefined) {
const { globalDeclarations } = analyzedDocument
Expand Down