Skip to content
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
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,35 @@ Open the command palette (`⌘ + ⇧ + p`) and simply call the `Start Protein Vi

Right-click on the file or selection of files in the file editor and select `Launch Protein Viewer from File(s)`

You can also launch programmatically (for example from bash or Python) via a VS Code URI:

`vscode://ArianJamasb.protein-viewer/open?file=/absolute/path/to/first.pdb&file=/absolute/path/to/second.cif`

The `file` query parameter can be repeated for multiple files.

Example from bash (using Python for URL encoding):

```bash
python - <<'PY'
import urllib.parse
import webbrowser
files = ["/abs/a.pdb", "/abs/b.cif"]
query = "&".join(f"file={urllib.parse.quote(f)}" for f in files)
webbrowser.open(f"vscode://ArianJamasb.protein-viewer/open?{query}")
PY
```

Example from Python:

```python
import urllib.parse
import webbrowser

files = ["/abs/a.pdb", "/abs/b.cif"]
query = "&".join(f"file={urllib.parse.quote(f)}" for f in files)
webbrowser.open(f"vscode://ArianJamasb.protein-viewer/open?{query}")
```

**✅ Supported Formats**

* `.pdb`
Expand Down
31 changes: 29 additions & 2 deletions out/extension.js

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

2 changes: 1 addition & 1 deletion out/extension.js.map

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

20 changes: 20 additions & 0 deletions out/test/suite/uri-handler.test.js

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

1 change: 1 addition & 0 deletions out/test/suite/uri-handler.test.js.map

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

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
"onCommand:protein-viewer.start",
"onCommand:protein-viewer.activateFromFiles",
"onCommand:protein-viewer.activateFromFolder",
"onCommand:protein-viewer.ESMFold"
"onCommand:protein-viewer.ESMFold",
"onUri"
],
"main": "./out/extension.js",
"contributes": {
Expand Down
29 changes: 28 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import fetch from 'node-fetch';
import * as vscode from 'vscode';
import { ProteinViewerPanel } from "./panels/ProteinViewerPanel";
const path = require('node:path');
const URI_SCHEME_PATTERN = /^[a-zA-Z][a-zA-Z0-9+.-]*:/;

export async function activate(context: vscode.ExtensionContext) {

Expand All @@ -17,7 +18,10 @@ export async function activate(context: vscode.ExtensionContext) {
const activateFromFiles = vscode.commands.registerCommand("protein-viewer.activateFromFiles", (file_uri: vscode.Uri, selectedFiles: vscode.Uri[]) => {
console.log(file_uri);
console.log(selectedFiles);
ProteinViewerPanel.renderFromFiles(context.extensionUri, selectedFiles);
const filesToOpen = selectedFiles?.length ? selectedFiles : (file_uri ? [file_uri] : []);
if (filesToOpen.length > 0) {
ProteinViewerPanel.renderFromFiles(context.extensionUri, filesToOpen);
}
});

const activateFromFolder = vscode.commands.registerCommand("protein-viewer.activateFromFolder", (folder_uri: vscode.Uri) => {
Expand All @@ -39,11 +43,22 @@ export async function activate(context: vscode.ExtensionContext) {

});
});
const uriHandler = vscode.window.registerUriHandler({
handleUri(uri: vscode.Uri) {
const filesToOpen = getFilesFromLaunchUri(uri);
if (filesToOpen.length === 0) {
vscode.window.showErrorMessage("Protein Viewer: no files were provided in URI. Use ?file=/abs/path/to/file.pdb (repeat file for multiple files).");
return;
}
ProteinViewerPanel.renderFromFiles(context.extensionUri, filesToOpen);
}
});
//context.subscriptions.push(...[helloCommand, activateFromFile]);
context.subscriptions.push(helloCommand);
context.subscriptions.push(activateFromFiles);
context.subscriptions.push(activateFromFolder);
context.subscriptions.push(ESMFold);
context.subscriptions.push(uriHandler);
}

// this method is called when your extension is deactivated
Expand Down Expand Up @@ -97,3 +112,15 @@ async function getfold(sequence: string | undefined) {
const body = await response.text();
return body
}

export function getFilesFromLaunchUri(uri: vscode.Uri): vscode.Uri[] {
if (uri.path !== "/open") {
return [];
}
const params = new URLSearchParams(uri.query);
const files = params.getAll("file")
.map(file => file.trim())
.filter(file => file.length > 0)
.map(file => file.match(URI_SCHEME_PATTERN) ? vscode.Uri.parse(file, true) : vscode.Uri.file(file));
return files;
}
19 changes: 19 additions & 0 deletions src/test/suite/uri-handler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import * as assert from 'assert';
import * as vscode from 'vscode';
import { getFilesFromLaunchUri } from '../../extension';

suite('URI Handler Test Suite', () => {
test('parses repeated file params from launch uri', () => {
const launchUri = vscode.Uri.parse('vscode://ArianJamasb.protein-viewer/open?file=%2Ftmp%2Fa.pdb&file=%2Ftmp%2Fb.cif');
const files = getFilesFromLaunchUri(launchUri);
assert.strictEqual(files.length, 2);
assert.strictEqual(files[0].fsPath, '/tmp/a.pdb');
assert.strictEqual(files[1].fsPath, '/tmp/b.cif');
});

test('ignores non-open uri path', () => {
const launchUri = vscode.Uri.parse('vscode://ArianJamasb.protein-viewer/other?file=%2Ftmp%2Fa.pdb');
const files = getFilesFromLaunchUri(launchUri);
assert.strictEqual(files.length, 0);
});
});