Skip to content
This repository has been archived by the owner on Oct 15, 2024. It is now read-only.

Commit

Permalink
feat: add lupdate support
Browse files Browse the repository at this point in the history
Fix #282
  • Loading branch information
seanwu1105 committed Mar 3, 2023
1 parent 9556d61 commit 8b6829e
Show file tree
Hide file tree
Showing 17 changed files with 232 additions and 18 deletions.
53 changes: 48 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,27 +106,32 @@
"commands": [
{
"command": "qtForPython.compileResource",
"title": "Compile Qt Resource File",
"title": "Compile Qt Resource File (rcc)",
"category": "Qt for Python"
},
{
"command": "qtForPython.compileUi",
"title": "Compile Qt UI File",
"title": "Compile Qt UI File (uic)",
"category": "Qt for Python"
},
{
"command": "qtForPython.createUi",
"title": "Create Qt UI File",
"title": "Create Qt UI File (designer)",
"category": "Qt for Python"
},
{
"command": "qtForPython.editUi",
"title": "Edit Qt UI File",
"title": "Edit Qt UI File (designer)",
"category": "Qt for Python"
},
{
"command": "qtForPython.previewQml",
"title": "Preview QML File",
"title": "Preview QML File (qml)",
"category": "Qt for Python"
},
{
"command": "qtForPython.lupdate",
"title": "Update Qt Translation File (lupdate)",
"category": "Qt for Python"
}
],
Expand Down Expand Up @@ -156,6 +161,11 @@
"command": "qtForPython.previewQml",
"when": "resourceLangId == qml",
"group": "qtForPython"
},
{
"command": "qtForPython.lupdate",
"when": "resourceLangId == python || resourceLangId == qml || (resourceLangId == xml && resourceExtname == .ui)",
"group": "qtForPython"
}
],
"explorer/context": [
Expand Down Expand Up @@ -183,6 +193,11 @@
"command": "qtForPython.previewQml",
"when": "resourceLangId == qml",
"group": "qtForPython"
},
{
"command": "qtForPython.lupdate",
"when": "resourceLangId == python || resourceLangId == qml || (resourceLangId == xml && resourceExtname == .ui)",
"group": "qtForPython"
}
],
"editor/title": [
Expand Down Expand Up @@ -210,6 +225,11 @@
"command": "qtForPython.previewQml",
"when": "resourceLangId == qml",
"group": "qtForPython"
},
{
"command": "qtForPython.lupdate",
"when": "resourceLangId == python || resourceLangId == qml || (resourceLangId == xml && resourceExtname == .ui)",
"group": "qtForPython"
}
],
"editor/context": [
Expand Down Expand Up @@ -237,6 +257,11 @@
"command": "qtForPython.previewQml",
"when": "resourceLangId == qml",
"group": "qtForPython"
},
{
"command": "qtForPython.lupdate",
"when": "resourceLangId == python || resourceLangId == qml || (resourceLangId == xml && resourceExtname == .ui)",
"group": "qtForPython"
}
]
},
Expand Down Expand Up @@ -352,6 +377,24 @@
"default": [],
"markdownDescription": "The options passed to `pyside6-qml` executable for QML preview. See [here](https://github.com/seanwu1105/vscode-qt-for-python#predefined-variables) for a detailed list of predefined variables.",
"scope": "resource"
},
"qtForPython.lupdate.path": {
"type": "string",
"default": "",
"markdownDescription": "The path to Qt `lupdate` executable. Set to empty string to automatically resolve from the installed Python package. See [here](https://github.com/seanwu1105/vscode-qt-for-python#predefined-variables) for a detailed list of predefined variables.",
"scope": "resource"
},
"qtForPython.lupdate.options": {
"type": "array",
"items": {
"type": "string"
},
"default": [
"-ts",
"${resourceDirname}${pathSeparator}${resourceBasenameNoExtension}.ts"
],
"markdownDescription": "The options passed to Qt `lupdate` executable. See [here](https://github.com/seanwu1105/vscode-qt-for-python#predefined-variables) for a detailed list of predefined variables.",
"scope": "resource"
}
}
},
Expand Down
1 change: 1 addition & 0 deletions python/.coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ omit =
scripts/uic.py
scripts/designer.py
scripts/qml.py
scripts/lupdate.py

[report]
fail_under = 100
Expand Down
2 changes: 0 additions & 2 deletions python/scripts/designer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# pylint: disable=import-error

import re
import sys

from utils import is_installed
Expand All @@ -12,5 +11,4 @@
from PySide2.scripts.pyside_tool import designer
else:
sys.exit("No rcc can be found in current Python environment.")
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(designer())
20 changes: 20 additions & 0 deletions python/scripts/lupdate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# pylint: disable=import-error

import sys

from utils import is_installed

if __name__ == "__main__":
if is_installed("PySide6"):
from PySide6.scripts.pyside_tool import lupdate
elif is_installed("PySide2"):
sys.argv[0] = "pyside2-lupdate"
from PySide2.scripts.pyside_tool import main as lupdate
elif is_installed("PyQt6"):
from PyQt6.lupdate.pylupdate import main as lupdate
elif is_installed("PyQt5"):
from PyQt5.pylupdate_main import main as lupdate
else:
sys.exit("No lupdate can be found in current Python environment.")

sys.exit(lupdate())
2 changes: 0 additions & 2 deletions python/scripts/qml.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# pylint: disable=import-error

import re
import sys

from utils import is_installed
Expand All @@ -10,5 +9,4 @@
from PySide6.scripts.pyside_tool import qml
else:
sys.exit("No pyside6-qml can be found in current Python environment.")
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(qml())
2 changes: 0 additions & 2 deletions python/scripts/qmlls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# pylint: disable=import-error

import re
import sys

from utils import is_installed
Expand All @@ -10,5 +9,4 @@
from PySide6.scripts.pyside_tool import qmlls
else:
sys.exit("No qmlls can be found in current Python environment.")
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(qmlls())
2 changes: 0 additions & 2 deletions python/scripts/rcc.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# pylint: disable=import-error

import re
import sys

from utils import is_installed
Expand All @@ -14,5 +13,4 @@
from PyQt5.pyrcc_main import main as rcc
else:
sys.exit("No rcc can be found in current Python environment.")
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(rcc())
2 changes: 0 additions & 2 deletions python/scripts/uic.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# pylint: disable=import-error

import re
import sys

from utils import is_installed
Expand All @@ -16,5 +15,4 @@
from PyQt5.uic.pyuic import main as uic
else:
sys.exit("No rcc can be found in current Python environment.")
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
sys.exit(uic())
35 changes: 35 additions & 0 deletions python/tests/test_lupdate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os
import subprocess

from tests import ASSETS_DIR, SCRIPTS_DIR


def test_lupdate_help():
result = invoke_lupdate_py(["-help"])
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0


def test_lupdate_sample_py():
filename = "sample.py"
result = invoke_lupdate_py(
[get_assets_path(filename), "-ts", get_assets_path("sample.ts")]
)
assert result.returncode == 0
assert len(result.stdout.decode("utf-8")) > 0
assert os.path.exists(get_assets_path("sample.ts"))

os.remove(get_assets_path("sample.ts"))


def invoke_lupdate_py(args: list[str]):
return subprocess.run(
["poetry", "run", "python", "lupdate.py", *args],
cwd=SCRIPTS_DIR,
capture_output=True,
check=True,
)


def get_assets_path(filename: str) -> str:
return os.path.join(ASSETS_DIR, "linguist", filename)
5 changes: 5 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { URI } from 'vscode-uri'
import { EXTENSION_NAMESPACE } from './constants'
import { createUi } from './designer/create-ui'
import { editUi } from './designer/edit-ui'
import { lupdate } from './lupdate/lupdate'
import { previewQml } from './qml/preview-qml'
import { compileResource } from './rcc/compile-resource'
import type { ErrorResult, SuccessResult } from './types'
Expand Down Expand Up @@ -51,6 +52,10 @@ const COMMANDS = [
name: 'previewQml',
callback: previewQml,
},
{
name: 'lupdate',
callback: lupdate,
},
] as const

type CommandCallbackValue = Awaited<
Expand Down
31 changes: 31 additions & 0 deletions src/lupdate/lupdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { firstValueFrom } from 'rxjs'
import type { CommandDeps } from '../commands'
import { getTargetDocumentUri } from '../commands'
import { run } from '../run'
import { getToolCommand$ } from '../tool-utils'

export async function lupdate({ extensionUri }: CommandDeps, ...args: any[]) {
const targetDocumentUriResult = getTargetDocumentUri(...args)

if (targetDocumentUriResult.kind !== 'Success') return targetDocumentUriResult

const sourceFile = targetDocumentUriResult.value

const getToolCommandResult = await firstValueFrom(
getToolCommand$({
tool: 'lupdate',
extensionUri,
resource: sourceFile,
}),
)

if (getToolCommandResult.kind !== 'Success') return getToolCommandResult

return run({
command: [
...getToolCommandResult.value.command,
sourceFile.fsPath,
...getToolCommandResult.value.options,
],
})
}
71 changes: 71 additions & 0 deletions src/test/suite/linguist/e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import * as assert from 'node:assert'
import * as path from 'node:path'
import { commands, window, workspace } from 'vscode'
import { URI } from 'vscode-uri'
import { EXTENSION_NAMESPACE } from '../../../constants'
import {
E2E_TIMEOUT,
forceDeleteFile,
setupE2EEnvironment,
TEST_ASSETS_PATH,
waitFor,
} from '../test-utils'

suite('linguist/e2e', () => {
suiteSetup(async function () {
this.timeout(E2E_TIMEOUT)
await setupE2EEnvironment()
})

suite('command palette', () => {
suite('when a Python file is open', () => {
const sampleFilenameNoExt = 'sample'

setup(async function () {
this.timeout(E2E_TIMEOUT)

await removeGeneratedFile(sampleFilenameNoExt)

const document = await workspace.openTextDocument(
URI.file(
path.resolve(
TEST_ASSETS_PATH,
'linguist',
`${sampleFilenameNoExt}.py`,
),
),
)
await window.showTextDocument(document)
})

teardown(async function () {
this.timeout(E2E_TIMEOUT)
await removeGeneratedFile(sampleFilenameNoExt)
})

test('should run lupdate command', async () => {
await commands.executeCommand(`${EXTENSION_NAMESPACE}.lupdate`)

return waitFor(async () => {
const readResult = await workspace.fs.readFile(
URI.file(
path.resolve(
TEST_ASSETS_PATH,
'linguist',
`${sampleFilenameNoExt}.ts`,
),
),
)

assert.ok(readResult.byteLength > 0)
})
}).timeout(E2E_TIMEOUT)
}).timeout(E2E_TIMEOUT)
}).timeout(E2E_TIMEOUT)
}).timeout(E2E_TIMEOUT)

async function removeGeneratedFile(sampleFilenameNoExt: string) {
return forceDeleteFile(
path.resolve(TEST_ASSETS_PATH, 'linguist', `${sampleFilenameNoExt}.ts`),
)
}
12 changes: 12 additions & 0 deletions src/test/suite/linguist/lupdate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import * as assert from 'node:assert'
import { commands } from 'vscode'
import { EXTENSION_NAMESPACE } from '../../../constants'

suite('lupdate', () => {
test('should include the command', async () =>
assert.ok(
(await commands.getCommands(true)).includes(
`${EXTENSION_NAMESPACE}.lupdate`,
),
))
})
4 changes: 2 additions & 2 deletions src/test/suite/uic/compile-ui.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ suite('compile-ui/e2e', () => {
))

suite('command palette', () => {
suite('when a ui file is open', () => {
suite('when an ui file is open', () => {
const sampleFilenameNoExt = 'sample'

setup(async function () {
Expand Down Expand Up @@ -63,7 +63,7 @@ suite('compile-ui/e2e', () => {
assert.ok(readResult.byteLength > 0)
})
}).timeout(E2E_TIMEOUT)
})
}).timeout(E2E_TIMEOUT)
}).timeout(E2E_TIMEOUT)
}).timeout(E2E_TIMEOUT)

Expand Down
Loading

0 comments on commit 8b6829e

Please sign in to comment.