Skip to content

Commit

Permalink
Chore: Add imports from the beginning
Browse files Browse the repository at this point in the history
  • Loading branch information
idillon-sfl committed Nov 28, 2023
1 parent 4d0943a commit 15fdd67
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 109 deletions.
63 changes: 11 additions & 52 deletions server/src/__tests__/embedded-languages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { generateParser } from '../tree-sitter/parser'
import { FIXTURE_DOCUMENT } from './fixtures/fixtures'
import { TextDocument } from 'vscode-languageserver-textdocument'
import { type EmbeddedLanguageType } from '../lib/src/types/embedded-languages'
import { imports } from '../embedded-languages/python-support'

describe('Embedded Language Documents file management', () => {
beforeAll(async () => {
Expand Down Expand Up @@ -117,22 +118,22 @@ describe('Create various basic embedded python documents', () => {
[
'anonymous',
'python(){\n pass\n}',
'def _ ():\n pass\n '
`${imports}def _ ():\n pass\n `
],
[
'named with python keyword',
'python foo (){\n pass\n}',
'def foo ():\n pass\n '
`${imports}def foo ():\n pass\n `
],
[
'empty',
'python(){\n}',
'def _ ():\n pass\n '
`${imports}def _ ():\n pass\n `
],
[
'with def keyword',
'def foo():\n pass',
'def foo():\n pass'
`${imports}def foo():\n pass`
]
])('%s', async (description, input, result) => {
const embeddedContent = await createEmbeddedContent(input, 'python')
Expand All @@ -155,31 +156,31 @@ describe('Create Python embedded language content with inline Python', () => {
'basic',
// eslint-disable-next-line no-template-curly-in-string
'FOO = \'${@"BAR"}\'',
' \n\n"BAR"\n '
`${imports} \n\n"BAR"\n `
],
[
'with spacing',
// eslint-disable-next-line no-template-curly-in-string
'FOO = \'${@ "BAR" }\'',
' \n \n"BAR" \n '
`${imports} \n \n"BAR" \n `
],
[
'multiline',
// eslint-disable-next-line no-template-curly-in-string
'FOO = \'${@"BAR"}\' \\\n1 \\\n2"',
' \n\n"BAR"\n \n \n '
`${imports} \n\n"BAR"\n \n \n `
],
[
'with two embedded python regions',
// eslint-disable-next-line no-template-curly-in-string
'FOO = \'${@"BAR"}${@"BAR"}\'',
' \n\n"BAR"\n \n\n"BAR"\n '
`${imports} \n\n"BAR"\n \n\n"BAR"\n `
],
[
'without surrounding quotes',
// eslint-disable-next-line no-template-curly-in-string
'inherit ${@"test"}',
' \n\n"test"\n'
`${imports} \n\n"test"\n`
]
/* // This is not yet supported by tree-sitter
[
Expand All @@ -194,48 +195,6 @@ describe('Create Python embedded language content with inline Python', () => {
})
})

describe('Create Python embedded language content with imports', () => {
beforeAll(async () => {
if (!analyzer.hasParser()) {
const parser = await generateParser()
analyzer.initialize(parser)
}
analyzer.resetAnalyzedDocuments()
await embeddedLanguageDocsManager.setStoragePath(__dirname)
})

test.each([
[
'with bb',
'python(){\n bb.parse.vars_from_file("test")\n}',
'import bb\nfrom bb import parse\nbb.parse = parse\ndef _ ():\n bb.parse.vars_from_file("test")\n '
],
[
'with d',
'python(){\n d.getVar("test")\n}',
'from bb import data_smart\nd = data_smart.DataSmart()\ndef _ ():\n d.getVar("test")\n '
],
[
'with e',
'python(){\n e.data.getVar("test")\n}',
'from bb import data_smart\nd = data_smart.DataSmart()\nfrom bb import event\ne = event.Event()\ne.data = d\ndef _ ():\n e.data.getVar("test")\n '
],
[
'with os',
'python(){\n os.path.dirname("test")\n}',
'import os\ndef _ ():\n os.path.dirname("test")\n '
],
[
'with combination (d and bb)',
'python(){\n d.getVar("test")\n bb.parse.vars_from_file("test")\n}',
'from bb import data_smart\nd = data_smart.DataSmart()\nimport bb\nfrom bb import parse\nbb.parse = parse\ndef _ ():\n d.getVar("test")\n bb.parse.vars_from_file("test")\n '
]
])('%s', async (description, input, result) => {
const embeddedContent = await createEmbeddedContent(input, 'python')
expect(embeddedContent).toEqual(result)
})
})

const createEmbeddedContent = async (content: string, language: EmbeddedLanguageType): Promise<string | undefined> => {
const uri = randomUUID()
const document = TextDocument.create(uri, 'bitbake', 1, content)
Expand All @@ -253,7 +212,7 @@ const createEmbeddedContent = async (content: string, language: EmbeddedLanguage
}

const expectedPythonEmbeddedLanguageDoc =
`
`${imports}
def do_foo():
print('123')
Expand Down
79 changes: 22 additions & 57 deletions server/src/embedded-languages/python-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,52 @@ import * as TreeSitterUtils from '../tree-sitter/utils'
import { embeddedLanguageDocsManager } from './documents-manager'
import { type EmbeddedLanguageDoc, insertTextIntoEmbeddedLanguageDoc, initEmbeddedLanguageDoc } from './utils'

export const imports = [
'import bb',
'from bb import data_smart',
'd = data_smart.DataSmart()',
'from bb import event',
'e = event.Event()',
'e.data = d',
'import os',
''
].join('\n')

export const generatePythonEmbeddedLanguageDoc = async (textDocument: TextDocument): Promise<void> => {
const analyzedDocument = analyzer.getAnalyzedDocument(textDocument.uri)
if (analyzedDocument === undefined) {
return
}
const imports = new Set<string>()
const embeddedLanguageDoc = initEmbeddedLanguageDoc(textDocument, 'python')
TreeSitterUtils.forEach(analyzedDocument.tree.rootNode, (node) => {
switch (node.type) {
case 'python_function_definition':
handlePythonFunctionDefinition(node, embeddedLanguageDoc, imports)
handlePythonFunctionDefinition(node, embeddedLanguageDoc)
return false
case 'anonymous_python_function':
handleAnonymousPythonFunction(node, embeddedLanguageDoc, imports)
handleAnonymousPythonFunction(node, embeddedLanguageDoc)
return false
case 'inline_python':
handleInlinePythonNode(node, embeddedLanguageDoc, imports)
handleInlinePythonNode(node, embeddedLanguageDoc)
return false
default:
return true
}
})
if (imports.size !== 0) {
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, 0, 0, [...imports].join('\n') + '\n')
}
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, 0, 0, imports)
await embeddedLanguageDocsManager.saveEmbeddedLanguageDoc(embeddedLanguageDoc)
}

const handlePythonFunctionDefinition = (node: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc, imports: Set<string>): void => {
const handlePythonFunctionDefinition = (node: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc): void => {
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, node.startIndex, node.endIndex, node.text)
node.children.forEach((child) => {
if (child.type === 'block') {
handleBlockNode(child, embeddedLanguageDoc, imports)
handleBlockNode(child, embeddedLanguageDoc)
}
})
}

const handleAnonymousPythonFunction = (node: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc, imports: Set<string>): void => {
const handleAnonymousPythonFunction = (node: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc): void => {
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, node.startIndex, node.endIndex, node.text)
node.children.forEach((child) => {
switch (child.type) {
Expand All @@ -72,15 +80,15 @@ const handleAnonymousPythonFunction = (node: SyntaxNode, embeddedLanguageDoc: Em
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, child.startIndex, child.endIndex, ' ')
break
case 'block':
handleBlockNode(child, embeddedLanguageDoc, imports)
handleBlockNode(child, embeddedLanguageDoc)
break
default:
break
}
})
}

const handleInlinePythonNode = (inlinePythonNode: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc, imports: Set<string>): void => {
const handleInlinePythonNode = (inlinePythonNode: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc): void => {
const openingNode = inlinePythonNode.child(0)
const pythonContentNode = inlinePythonNode.child(1)
const closingNode = inlinePythonNode.child(2)
Expand All @@ -98,56 +106,13 @@ const handleInlinePythonNode = (inlinePythonNode: SyntaxNode, embeddedLanguageDo
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, pythonContentNode.startIndex, pythonContentNode.startIndex, '\n') // prevent trailing spaces
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, pythonContentNode.startIndex, pythonContentNode.endIndex, pythonContentNode.text)
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, closingNode.startIndex, closingNode.endIndex, '\n')
handleBlockNode(pythonContentNode, embeddedLanguageDoc, imports)
handleBlockNode(pythonContentNode, embeddedLanguageDoc)
}

const handleBlockNode = (blockNode: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc, imports: Set<string>): void => {
const handleBlockNode = (blockNode: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc): void => {
if (blockNode.text === '') {
insertTextIntoEmbeddedLanguageDoc(embeddedLanguageDoc, blockNode.startIndex, blockNode.endIndex, '\n pass')
}
handleImports(blockNode, imports)
}

const handleImports = (blockNode: SyntaxNode, imports: Set<string>): void => {
const importBb = (bbNode: SyntaxNode): void => {
if (bbNode.nextSibling?.type === '.' && bbNode.nextNamedSibling?.type === 'python_identifier') {
const importName = bbNode.nextNamedSibling.text
imports.add('import bb')
imports.add(`from bb import ${importName}`)
imports.add(`bb.${importName} = ${importName}`)
}
}

const importD = (): void => {
imports.add('from bb import data_smart')
imports.add('d = data_smart.DataSmart()')
}

const importE = (): void => {
importD()
imports.add('from bb import event')
imports.add('e = event.Event()')
imports.add('e.data = d')
}

const importOs = (): void => {
imports.add('import os')
}

TreeSitterUtils.forEach(blockNode, (child) => {
if (child.type === 'python_identifier') {
if (child.text === 'bb') {
importBb(child)
} else if (child.text === 'd') {
importD()
} else if (child.text === 'e') {
importE()
} else if (child.text === 'os') {
importOs()
}
}
return true
})
}

const handleOverrideNode = (overrideNode: SyntaxNode, embeddedLanguageDoc: EmbeddedLanguageDoc): void => {
Expand Down

0 comments on commit 15fdd67

Please sign in to comment.