Add vscode extension

This commit is contained in:
c-bata
2023-05-22 14:52:49 +09:00
parent 9394ef175a
commit f98ddc9784
21 changed files with 8570 additions and 22 deletions
+4
View File
@@ -29,4 +29,8 @@ rustlib/target/
rustlib/pkg/
# Others
.envrc
.idea/
.vscode/
.DS_Store
tmp/
+1 -1
View File
@@ -1,2 +1,2 @@
optuna_dashboard/ts/components/PlotlyDarkMode.ts
standalone_app/PlotlyDarkMode.ts
standalone_app/src/PlotlyDarkMode.ts
+50 -18
View File
@@ -1,8 +1,27 @@
# Developers Guide
## How to run
Thank you for your interest in contributing to the Optuna Dashboard project!
This document will provide you with an overview of the repository structure and how to build optuna-dashboard.
### Compiling TypeScript files
## Repository Structure
The repository is organized as follows:
```
.
├── optuna_dashboard/ # The Python package.
│ └── ts/ # TypeScript code for the Python package.
├── standalone_app/ # Standalone application that can be run in browser or within the WevView of the VSCode extension.
│ ├── browser_app_entry.tsx # Entry point for browser app, hosted on GitHub pages.
│ └── vscode_entry.tsx # Entry point for VSCode app, output placed under `vscode/assets`.
├── vscode/ # The VSCode extension.
└── rustlib/ # Rust library exporting Wasm functions.
└── pkg/ # Output directory for rustlib, installed from package.json via `"./rustlib/pkg"`.
```
## Python package
#### Compiling TypeScript files
Node.js v16 is required to compile TypeScript files.
@@ -29,7 +48,7 @@ $ npm run build:prd
</details>
### Building a Docker image
#### Building a Docker image
```
$ docker build -t optuna-dashboard .
@@ -43,7 +62,7 @@ You can use the Docker image like below:
$ docker run -it --rm -p 8080:8080 -v `pwd`:/app -w /app optuna-dashboard sqlite:///db.sqlite3
```
### Running dashboard server
#### Running dashboard server
```
$ pip install -e .
@@ -52,15 +71,15 @@ $ OPTUNA_DASHBOARD_DEBUG=1 optuna-dashboard sqlite:///db.sqlite3
Note that `OPTUNA_DASHBOARD_DEBUG=1` makes the server will automatically restart when the source codes are changed.
## Running tests, lint checks and formatters
### Running tests, lint checks and formatters
### Running Python unit tests
#### Running Python unit tests
```
$ python -m unittest
```
### Running visual regression tests using pyppeteer
#### Running visual regression tests using pyppeteer
Please run following commands, then check screenshots in `tmp/` directory.
@@ -71,7 +90,7 @@ $ python hack/visual_regression_test.py --output-dir tmp
Note: When you run pyppeteer for the first time, it downloads the latest version of Chromium (~150MB) if it is not found on your system.
### Linters (flake8, black and mypy)
#### Linters (flake8, black and mypy)
```
$ pip install -r requirements.txt
@@ -81,21 +100,14 @@ $ isort . --check
$ mypy optuna_dashboard python_tests
```
### Auto-formatting TypeScript files (by prettier)
#### Auto-formatting Python and TypeScript files
```
$ npm run fmt
```
### Auto-formatting Python files
```
$ black .
$ isort .
$ make fmt
```
## Release the new version
### Release the new version
The release process(compiling TypeScript files, packaging Python distributions and uploading to PyPI) is fully automated by GitHub Actions.
@@ -103,3 +115,23 @@ The release process(compiling TypeScript files, packaging Python distributions a
2. Create a git tag (e.g. v0.8.0) and push it to GitHub. If succeeded, GitHub Action will build sdist/wheel packages and create a draft GitHub release.
3. Edit a GitHub release, generate release note, write highlights of this release if needed, and mark "Create [a discussion](https://github.com/optuna/optuna-dashboard/discussions/categories/announcements) for this release" checkbox. Then make it publish. GitHub Action will release the new version to PyPI.
## Standalone Single-page Application
### Compiling Rust library and TypeScript files
Please install [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/) and execute the following command.
```
$ make serve-browser-app
```
Open http://127.0.0.1:9000/
## VSCode Extension
```
$ npm i -g vsce
$ make vscode-extension
```
+54
View File
@@ -0,0 +1,54 @@
.DEFAULT_GOAL := sdist
PYTHON ?= python3
MODE ?= dev
RST_FILES := $(shell find docs -name '*.rst')
PYTHON_FILES := $(shell find optuna_dashboard/ -name '*.py')
DASHBOARD_TS_IN := $(shell find ./optuna_dashboard -name '*.ts' -o -name '*.tsx')
DASHBOARD_TS_OUT = optuna_dashboard/public/bundle.js optuna_dashboard/public/favicon.ico
RUSTLIB_OUT = rustlib/pkg/optuna_wasm.js rustlib/pkg/optuna_wasm_bg.wasm rustlib/pkg/package.json
STANDALONE_OUT = standalone_app/public/bundle.js vscode/assets/bundle.js
$(RUSTLIB_OUT): rustlib/src/*.rs rustlib/Cargo.toml
cd rustlib && wasm-pack build --target web
$(STANDALONE_OUT): $(RUSTLIB_OUT)
cd standalone_app && npm install && npm run build:$(MODE)
$(DASHBOARD_TS_OUT): $(DASHBOARD_TS_IN)
npm install && npm run build:$(MODE)
.PHONY: watch-standalone-app
watch-standalone-app: standalone_app/public/bundle.js
cd standalone_app && npm run watch
.PHONY: serve-browser-app
serve-browser-app: standalone_app/public/bundle.js
$(PYTHON) -m http.server 9000 --directory ./standalone_app/
.PHONY: vscode-extension
vscode-extension: vscode/assets/bundle.js
cd vscode && vsce package
.PHONY: sdist
sdist: pyproject.toml $(DASHBOARD_TS_OUT)
python setup.py sdist
.PHONY: wheel
wheel: pyproject.toml $(DASHBOARD_TS_OUT)
python setup.py bdist_wheel
.PHONY: docs
docs: docs/conf.py $(RST_FILES)
cd docs && make html
.PHONY: fmt
fmt:
npm run fmt
black ./optuna_dashboard/ ./python_tests/
isort .
.PHONY: clean
clean:
rm -rf optuna_dashboard/public/ doc/_build/
rm -rf rustlib/pkg standalone_app/public/ vscode/assets/ vscode/*.vsix
+1 -1
View File
@@ -5,7 +5,7 @@
"description": "Dashboard for Optuna",
"main": "index.js",
"scripts": {
"fmt": "prettier --write \"optuna_dashboard/ts/**/*.{ts,tsx}\" \"typescript_tests/*.{ts,tsx}\" \"standalone_app/*.{ts,tsx}\"",
"fmt": "prettier --write \"optuna_dashboard/ts/**/*.{ts,tsx}\" \"typescript_tests/**/*.{ts,tsx}\" \"standalone_app/src/**/*.{ts,tsx}\"",
"lint": "npm run lint:eslint && npm run lint:fmt",
"lint:eslint": "eslint . --ext .ts,.tsx",
"lint:fmt": "prettier --list-different \"optuna_dashboard/ts/**/*.{ts,tsx}\" \"typescript_tests/*.{ts,tsx}\" \"standalone_app/*.{ts,tsx}\"",
+1 -1
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="utf-8" />
<title>Optuna Dashboard (Wasm ver.)</title>
<script defer type="module" src="/bundle.js"></script>
<script defer type="module" src="/public/bundle.js"></script>
<link rel="icon" href="/favicon.ico" />
</head>
<body>
+1 -1
View File
@@ -5,7 +5,7 @@
"description": "",
"scripts": {
"watch": "NODE_ENV=development TYPESCRIPT_LOADER=esbuild-loader webpack --watch",
"serve": "python3 -m http.server 9000 --directory ./public",
"serve": "python3 -m http.server 9000 --directory .",
"build:dev": "NODE_ENV=development TYPESCRIPT_LOADER=esbuild-loader webpack",
"build:prd": "NODE_ENV=production webpack"
},
+22
View File
@@ -0,0 +1,22 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"rules": {
"@typescript-eslint/naming-convention": "warn",
"@typescript-eslint/semi": "warn",
"curly": "warn",
"eqeqeq": "warn",
"no-throw-literal": "warn",
"semi": "off"
},
"ignorePatterns": [
"**/*.d.ts"
]
}
+13
View File
@@ -0,0 +1,13 @@
.vscode/**
.vscode-test-web/**
src/**
out/**
node_modules/**
.gitignore
vsc-extension-quickstart.md
webpack.config.js
.yarnrc
**/tsconfig.json
**/.eslintrc.json
**/*.map
**/*.ts
+9
View File
@@ -0,0 +1,9 @@
# Change Log
All notable changes to the "optuna-dashboard" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Optuna Development Team
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+25
View File
@@ -0,0 +1,25 @@
# optuna-dashboard README
## Features
VSCode Extension that launches Optuna Dashboard (Wasm ver.).
## Extension Settings
Nothing to configure.
## Known Issues
* ...
## Release Notes
Users appreciate release notes as you update your extension.
### 0.0.1
Initial Release
---
**Enjoy!**
Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

+8039
View File
File diff suppressed because it is too large Load Diff
+73
View File
@@ -0,0 +1,73 @@
{
"name": "optuna-dashboard",
"displayName": "Optuna Dashboard",
"description": "Web Dashboard for Optuna",
"version": "0.0.1",
"license": "MIT",
"icon": "images/optuna-logo.png",
"engines": {
"vscode": "^1.78.0"
},
"homepage": "https://optuna.org/",
"bugs": {
"url": "https://github.com/optuna/optuna-dashboard/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/optuna/optuna-dashboard.git"
},
"categories": [
"Machine Learning",
"Visualization"
],
"keywords": [
"optuna",
"optuna dashboard",
"sqlite",
"sqlite3"
],
"activationEvents": [],
"browser": "./dist/web/extension.js",
"contributes": {
"commands": [
{
"command": "optuna-dashboard.openOptunaDashboard",
"title": "Open in Optuna Dashboard"
}
],
"menus": {
"explorer/context": [
{
"command": "optuna-dashboard.openOptunaDashboard",
"when": "resourceExtname == .db || resourceExtname == .sqlite3"
}
]
}
},
"scripts": {
"test": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. --extensionTestsPath=dist/web/test/suite/index.js",
"pretest": "npm run compile-web",
"vscode:prepublish": "npm run package-web",
"compile-web": "webpack",
"watch-web": "webpack --watch",
"package-web": "webpack --mode production --devtool hidden-source-map",
"lint": "eslint src --ext ts",
"run-in-browser": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. ."
},
"devDependencies": {
"@types/vscode": "^1.78.0",
"@types/mocha": "^10.0.1",
"eslint": "^8.39.0",
"@typescript-eslint/eslint-plugin": "^5.59.1",
"@typescript-eslint/parser": "^5.59.1",
"mocha": "^10.2.0",
"typescript": "^5.0.4",
"@vscode/test-web": "^0.0.43",
"ts-loader": "^9.4.2",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.2",
"@types/webpack-env": "^1.18.0",
"assert": "^2.0.0",
"process": "^0.11.10"
}
}
+79
View File
@@ -0,0 +1,79 @@
import * as vscode from "vscode"
export function activate(context: vscode.ExtensionContext) {
console.log(
'Congratulations, your extension "optuna-dashboard" is now active in the web extension host!'
)
let disposable = vscode.commands.registerCommand(
"optuna-dashboard.openOptunaDashboard",
async (fileUri: vscode.Uri) => {
// In VSCode, the path separator of fileUri is always '/'
// even when using Windows.
const title = fileUri.path.split("/").pop() || "Optuna Dashboard"
const panel = vscode.window.createWebviewPanel(
"optunaDashboard",
title,
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true,
}
)
const indexJsUri = vscode.Uri.joinPath(
context.extensionUri,
"assets",
"bundle.js"
)
const appPath = panel.webview.asWebviewUri(indexJsUri)
panel.webview.html = getWebviewContent(appPath)
const storageContentBase64 = await readFileAsBase64(fileUri)
panel.webview.postMessage({
type: "optunaStorage",
content: storageContentBase64,
})
vscode.window.showInformationMessage(
`Starting Optuna Dashboard for ${fileUri}`
)
}
)
context.subscriptions.push(disposable)
}
async function readFileAsBase64(uri: vscode.Uri): Promise<string> {
const uint8Array = await vscode.workspace.fs.readFile(uri)
const base64 = uint8ArrayToBase64(uint8Array)
return base64
}
function uint8ArrayToBase64(uint8Array: Uint8Array): string {
const binString = Array.prototype.map
.call(uint8Array, function (ch) {
return String.fromCharCode(ch)
})
.join("")
return btoa(binString)
}
function getWebviewContent(indexJsUri: vscode.Uri): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Optuna Dashboard (Wasm ver.)</title>
<script type="module" crossorigin src="${indexJsUri}"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
`
}
export function deactivate() {}
@@ -0,0 +1,15 @@
import * as assert from "assert"
// You can import and use all API from the 'vscode' module
// as well as import your extension to test it
import * as vscode from "vscode"
// import * as myExtension from '../../extension';
suite("Web Extension Test Suite", () => {
vscode.window.showInformationMessage("Start all tests.")
test("Sample test", () => {
assert.strictEqual(-1, [1, 2, 3].indexOf(5))
assert.strictEqual(-1, [1, 2, 3].indexOf(0))
})
})
+30
View File
@@ -0,0 +1,30 @@
// Imports mocha for the browser, defining the `mocha` global.
require("mocha/mocha")
export function run(): Promise<void> {
return new Promise((c, e) => {
mocha.setup({
ui: "tdd",
reporter: undefined,
})
// Bundles all files in the current directory matching `*.test`
const importAll = (r: __WebpackModuleApi.RequireContext) =>
r.keys().forEach(r)
importAll(require.context(".", true, /\.test$/))
try {
// Run the mocha test
mocha.run((failures) => {
if (failures > 0) {
e(new Error(`${failures} tests failed.`))
} else {
c()
}
})
} catch (err) {
console.error(err)
e(err)
}
})
}
+17
View File
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"outDir": "dist",
"lib": [
"ES2020", "WebWorker"
],
"sourceMap": true,
"rootDir": "src",
"strict": true /* enable all strict type-checking options */
/* Additional Checks */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
}
}
+44
View File
@@ -0,0 +1,44 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your web extension.
* `package.json` * this is the manifest file in which you declare your extension and command.
* `src/web/extension.ts` * this is the main file for the browser
* `webpack.config.js` * the webpack config file for the web main
## Setup
* install the recommended extensions (amodio.tsl-problem-matcher and dbaeumer.vscode-eslint)
## Get up and running the Web Extension
* Run `npm install`.
* Place breakpoints in `src/web/extension.ts`.
* Debug via F5 (Run Web Extension).
* Execute extension code via `F1 > Hello world`.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/web/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
## Run tests
* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`.
* Press `F5` to run the tests in a new window with your extension loaded.
* See the output of the test result in the debug console.
* Make changes to `src/web/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder.
* The provided test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.
## Go further
* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
* Check out the [Web Extension Guide](https://code.visualstudio.com/api/extension-guides/web-extensions).
* [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
* Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
+71
View File
@@ -0,0 +1,71 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
//@ts-check
'use strict';
//@ts-check
/** @typedef {import('webpack').Configuration} WebpackConfig **/
const path = require('path');
const webpack = require('webpack');
/** @type WebpackConfig */
const webExtensionConfig = {
mode: 'none', // this leaves the source code as close as possible to the original (when packaging we set this to 'production')
target: 'webworker', // extensions run in a webworker context
entry: {
'extension': './src/web/extension.ts',
'test/suite/index': './src/web/test/suite/index.ts'
},
output: {
filename: '[name].js',
path: path.join(__dirname, './dist/web'),
libraryTarget: 'commonjs',
devtoolModuleFilenameTemplate: '../../[resource-path]'
},
resolve: {
mainFields: ['browser', 'module', 'main'], // look for `browser` entry point in imported node modules
extensions: ['.ts', '.js'], // support ts-files and js-files
alias: {
// provides alternate implementation for node module and source files
},
fallback: {
// Webpack 5 no longer polyfills Node.js core modules automatically.
// see https://webpack.js.org/configuration/resolve/#resolvefallback
// for the list of Node.js core module polyfills.
'assert': require.resolve('assert')
}
},
module: {
rules: [{
test: /\.ts$/,
exclude: /node_modules/,
use: [{
loader: 'ts-loader'
}]
}]
},
plugins: [
new webpack.optimize.LimitChunkCountPlugin({
maxChunks: 1 // disable chunks by default since web extensions must be a single bundle
}),
new webpack.ProvidePlugin({
process: 'process/browser', // provide a shim for the global `process` variable
}),
],
externals: {
'vscode': 'commonjs vscode', // ignored because it doesn't exist
},
performance: {
hints: false
},
devtool: 'nosources-source-map', // create a source map that points to the original source file
infrastructureLogging: {
level: "log", // enables logging required for problem matchers
},
};
module.exports = [ webExtensionConfig ];