Skip to content
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
- Rename 'Upgrade firmware' to 'Update firmware'.
- Update instrument explorer as new discovered instruments come in rather than after discovery completes
- **tsp-toolkit-kic-cli** - Run discovery for lan and visa in parallel
### Added
- TSP language interop feature.
- **tsp-toolkit-language-interop** - Implement tsp language interop feature



Expand Down
70 changes: 69 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,11 @@
"title": "Reset to Defaults",
"category": "TSP",
"icon": "$(trash)"
},
{
"command": "tsp.convertToPython",
"title": "Convert TSP to Python",
"category": "TSP"
}
],
"configuration": {
Expand Down Expand Up @@ -483,13 +488,23 @@
"when": "activeEditor && ( resourceExtname == .tsp || resourceExtname == .tspa )",
"command": "tsp.sendFile",
"group": "navigation"
},
{
"when": "resourceExtname == .tsp",
"command": "tsp.convertToPython",
"group": "navigation"
}
],
"explorer/context": [
{
"when": "activeEditor && ( resourceExtname == .tsp || resourceExtname == .tspa )",
"command": "tsp.sendFile",
"group": "navigation"
},
{
"when": "resourceExtname == .tsp",
"command": "tsp.convertToPython",
"group": "navigation"
}
],
"editor/title/context": [
Expand Down Expand Up @@ -763,6 +778,7 @@
"copy-static": "copyfiles -u 1 src/**/*.{js,css} out"
},
"devDependencies": {
"@tektronix/tsp-language-interop-types": "0.1.0-0",
"@istanbuljs/nyc-config-typescript": "^1.0.0",
"@types/chai": "4.3.18",
"@types/mocha": "10.0.10",
Expand Down Expand Up @@ -803,6 +819,9 @@
"xml-js": "1.6.11"
},
"optionalDependencies": {
"@tektronix/tsp-language-interop-win32-x64": "0.1.0-0",
"@tektronix/tsp-language-interop-darwin-arm64": "0.1.0-0",
"@tektronix/tsp-language-interop-linux-x64": "0.1.0-0",
"@tektronix/kic-cli-darwin-arm64": "0.23.0-1",
"@tektronix/kic-cli-linux-x64": "0.23.0-1",
"@tektronix/kic-cli-win32-x64": "0.23.0-1",
Expand Down
13 changes: 13 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { CombinedScriptGenDataProvider } from "./combinedScriptGenDataProvider"
import { TriggerFlowWebViewManager } from "./triggerFlowWebViewManager"
import { GenericSessionStorage } from "./genericSessionStorage"
import { isMacOS } from "./utility"
import { convertTspToPython } from "./tspConverter"
import {
checkSystemDependencies,
checkVisaInstallation,
Expand All @@ -33,6 +34,7 @@ import {
} from "./dependencyChecker"

let _instrExplorer: InstrumentsExplorer
let _tspConverterDiagnostics: vscode.DiagnosticCollection

/**
* Represents a contributed TSP Toolkit configuration setting.
Expand Down Expand Up @@ -299,6 +301,11 @@ export async function activate(context: vscode.ExtensionContext) {
const LOGLOC: SourceLocation = { file: "extension.ts", func: "activate()" }
Log.info("TSP Toolkit activating", LOGLOC)

// Diagnostic collection for TSP → Python conversion warnings/errors
_tspConverterDiagnostics =
vscode.languages.createDiagnosticCollection("tsp-converter")
context.subscriptions.push(_tspConverterDiagnostics)

// Check for version updates and show announcement
Log.debug("Checking for version updates", LOGLOC)
void checkVersionAndShowAnnouncement(context)
Expand Down Expand Up @@ -517,6 +524,12 @@ export async function activate(context: vscode.ExtensionContext) {
await resetToolkitDefaults()
},
},
{
name: "tsp.convertToPython",
cb: async (e: vscode.Uri) => {
await convertTspToPython(e, _tspConverterDiagnostics)
},
},
])

Log.debug("Setting up HelpDocumentWebView", LOGLOC)
Expand Down
128 changes: 128 additions & 0 deletions src/tspConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import * as path from "path"
import * as vscode from "vscode"
import type {
Diagnostic,
TspInterop,
} from "@tektronix/tsp-language-interop-types"

function loadTspInterop(): TspInterop {
const packageName = `@tektronix/tsp-language-interop-${process.platform}-${process.arch}`

// eslint-disable-next-line @typescript-eslint/no-require-imports
const converter = require(packageName) as TspInterop

return converter
}

/**
* Read a .tsp file, convert it to Python via the native Rust addon, and open
* the result in a new editor tab. Any converter diagnostics are surfaced in
* the VS Code Problems panel.
*/
export async function convertTspToPython(
uri: vscode.Uri | undefined,
diagnosticCollection: vscode.DiagnosticCollection,
): Promise<void> {
// Allow invocation from command palette (no URI) by falling back to the
// active editor.
const fileUri =
uri ??
(vscode.window.activeTextEditor?.document.uri.fsPath.endsWith(".tsp")
? vscode.window.activeTextEditor.document.uri
: undefined)

if (!fileUri) {
vscode.window.showErrorMessage(
"No TSP file selected. Open a .tsp file or right-click it in the Explorer.",
)
return
}

const converter = loadTspInterop()
if (!converter) {
vscode.window.showErrorMessage(
"tsp-converter native addon could not be loaded. " +
"Please ensure the extension was built correctly.",
)
return
}

// Read source
let source: string
try {
source = (await vscode.workspace.fs.readFile(fileUri)).toString()
} catch (err) {
vscode.window.showErrorMessage(
`Could not read file: ${err instanceof Error ? err.message : String(err)}`,
)
return
}

// Derive a class name from the file name (e.g. "my_script.tsp" → "MyScript")
const baseName = path.basename(fileUri.fsPath, ".tsp")
const className = baseName
.split(/[_\-\s]+/)
.map((s) => s.charAt(0).toUpperCase() + s.slice(1))
.join("")

// Run converter
let result: { ok: boolean; code?: string; diagnostics: Diagnostic[] }
try {
result = converter.convertTspToPython(source, {
className,
scriptPath: fileUri.fsPath,
})
} catch (err) {
vscode.window.showErrorMessage(
`Converter error: ${err instanceof Error ? err.message : String(err)}`,
)
return
}

// Push diagnostics to the Problems panel
const vsDiagnostics = (result.diagnostics ?? []).map((d: Diagnostic) => {
const range = d.span
? new vscode.Range(
d.span.startLine - 1,
d.span.startColumn,
d.span.endLine - 1,
d.span.endColumn,
)
: new vscode.Range(0, 0, 0, 0)

const severity =
d.severity === "error"
? vscode.DiagnosticSeverity.Error
: d.severity === "warning"
? vscode.DiagnosticSeverity.Warning
: vscode.DiagnosticSeverity.Information

const diag = new vscode.Diagnostic(range, d.message, severity)
diag.code = d.code
if (d.hint)
diag.relatedInformation = [
new vscode.DiagnosticRelatedInformation(
new vscode.Location(fileUri, range),
d.hint,
),
]
return diag
})
diagnosticCollection.set(fileUri, vsDiagnostics)

if (!result.ok || !result.code) {
const errMsg = result.diagnostics?.[0]?.message ?? "Unknown error"
vscode.window.showErrorMessage(`TSP conversion failed: ${errMsg}`)
return
}

// Open generated Python in a new untitled editor tab
const doc = await vscode.workspace.openTextDocument({
language: "python",
content: result.code,
})
await vscode.window.showTextDocument(doc, {
viewColumn: vscode.ViewColumn.Beside,
preview: false,
})
}
Loading