implement playground
This commit is contained in:
parent
97843e9e66
commit
c19f44d7e4
79 changed files with 10153 additions and 1180 deletions
|
@ -4,5 +4,12 @@ default = "oksoftware"
|
|||
[registries.oksoftware]
|
||||
index = "https://ok.software/ok/_cargo-index.git" # Git
|
||||
|
||||
|
||||
[registries.crates-io]
|
||||
index = "https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[net]
|
||||
git-fetch-with-cli = true
|
||||
git-fetch-with-cli = true
|
||||
|
||||
[build]
|
||||
rustflags = "--cfg=web_sys_unstable_apis"
|
||||
|
|
20
.devcontainer/devcontainer.json
Normal file
20
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"image": "gcr.io/crosvm-infra/crosvm_dev:latest",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"rust-lang.rust-analyzer",
|
||||
"tamasfe.even-better-toml",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-python.vscode-pylance",
|
||||
"foxundermoon.shell-format",
|
||||
"timonwong.shellcheck"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": [
|
||||
// Allow a higher PID limit since we launch a lot of test processes.
|
||||
"--pids-limit=4096"
|
||||
],
|
||||
"updateContentCommand": "git config --global --add safe.directory '*' && git submodule update --init"
|
||||
}
|
27
.devcontainer/with_kvm/devcontainer.json
Normal file
27
.devcontainer/with_kvm/devcontainer.json
Normal file
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"image": "gcr.io/crosvm-infra/crosvm_dev:latest",
|
||||
"customizations": {
|
||||
"vscode": {
|
||||
"extensions": [
|
||||
"rust-lang.rust-analyzer",
|
||||
"tamasfe.even-better-toml",
|
||||
"esbenp.prettier-vscode",
|
||||
"ms-python.vscode-pylance",
|
||||
"foxundermoon.shell-format",
|
||||
"timonwong.shellcheck",
|
||||
"vadimcn.vscode-lldb",
|
||||
"GitHub.copilot",
|
||||
"llvm-vs-code-extensions.vscode-clangd",
|
||||
"aiqubit.claude"
|
||||
]
|
||||
}
|
||||
},
|
||||
"runArgs": [
|
||||
// Allow access to the kvm device so we can run VMs for testing
|
||||
"--device=/dev/kvm",
|
||||
"--group-add=kvm",
|
||||
// Allow a higher PID limit since we launch a lot of test processes.
|
||||
"--pids-limit=4096"
|
||||
],
|
||||
"updateContentCommand": "git config --global --add safe.directory '*' && git submodule update --init"
|
||||
}
|
24
.github/workflows/rust.yml
vendored
24
.github/workflows/rust.yml
vendored
|
@ -11,18 +11,28 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
targets: [wasm32-unknown-unknown, x86_64-unknown-linux-gnu]
|
||||
packages: [srclang, src-lsp-server, src-lsp-browser]
|
||||
toolchains: [stable, nightly]
|
||||
|
||||
container: rust:latest
|
||||
container: gcr.io/crosvm-infra/crosvm_dev:latest
|
||||
|
||||
steps:
|
||||
- name: Modify hosts file
|
||||
run: |
|
||||
echo "66.241.125.220 ok.software" >> /etc/hosts
|
||||
|
||||
- name: reinstall rustup
|
||||
run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
- name: rustup update
|
||||
run: |
|
||||
rustup toolchain install ${{ matrix.toolchains }}
|
||||
rustup target add ${{ matrix.targets }}
|
||||
- name: Build ${{ matrix.packages }} for ${{ matrix.targets }}
|
||||
run: cargo build --verbose --target ${{ matrix.targets }} -p ${{ matrix.packages }}
|
||||
- name: Test ${{ matrix.packages }} for ${{ matrix.targets }}
|
||||
run: cargo test --verbose --target ${{ matrix.targets }} -p ${{ matrix.packages }}
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
book
|
||||
target
|
||||
node_modules
|
9
.rust-toolchain.toml
Normal file
9
.rust-toolchain.toml
Normal file
|
@ -0,0 +1,9 @@
|
|||
|
||||
[toolchain]
|
||||
channel = "1.78"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
|
||||
targets = [
|
||||
"wasm32-unknown-unknown",
|
||||
"x86_64-unknown-linux-musl",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
]
|
1197
Cargo.lock
generated
1197
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
15
Cargo.toml
15
Cargo.toml
|
@ -3,6 +3,12 @@ name = "srclang"
|
|||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[workspace]
|
||||
members = [ "crates/src-collections",
|
||||
"crates/src-lsp-browser",
|
||||
"crates/src-lsp-server",
|
||||
]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[features]
|
||||
default = ["lalrpop"]
|
||||
|
@ -13,19 +19,16 @@ anyhow = "1.0.45"
|
|||
phf_codegen = "0.10"
|
||||
tiny-keccak = { version = "2", features = ["sha3"] }
|
||||
|
||||
|
||||
[dependencies]
|
||||
getrandom = { version = "0.2", features = ["js"] }
|
||||
salsa = { version = "0.1.0", registry = "oksoftware", package = "salsa-2022" }
|
||||
salsa-macros = { version = "0.1.0", registry = "oksoftware" , package = "salsa-2022-macros" }
|
||||
insta = "1.38.0"
|
||||
lalrpop = "0.20.2"
|
||||
lalrpop-util = { version = "0.20.2", features = ["lexer", "unicode"] }
|
||||
okstd = { version = "0.1.3", features = [], default-features = false, registry = "oksoftware" }
|
||||
proptest = "1.4.0"
|
||||
stringzilla = "3.8.1"
|
||||
okstd = { features = ["macros"], default-features = false, registry = "oksoftware", version = "0.1.9"}
|
||||
syn = "2.0.60"
|
||||
bitflags = "2.5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.38.0"
|
||||
proptest = "1.4.0"
|
||||
|
||||
|
|
47
Makefile.toml
Normal file
47
Makefile.toml
Normal file
|
@ -0,0 +1,47 @@
|
|||
[config]
|
||||
default_to_workspace = false
|
||||
skip_core_tasks = true
|
||||
|
||||
[tasks.deps]
|
||||
script = '''
|
||||
cargo install wasm-bindgen-cli --version 0.2.81 --registry crates-io
|
||||
npm install
|
||||
'''
|
||||
|
||||
[tasks.build-server]
|
||||
script = '''
|
||||
cargo build --release
|
||||
wasm-bindgen --out-dir ./packages/app/assets/wasm --target web --typescript ./target/wasm32-unknown-unknown/release/demo_lsp_browser.wasm
|
||||
'''
|
||||
|
||||
[tasks.build-app]
|
||||
script = '''
|
||||
npm run build --workspace=packages/app
|
||||
'''
|
||||
|
||||
[tasks.build]
|
||||
dependencies = ["build-server", "build-app"]
|
||||
|
||||
[tasks.clean-server]
|
||||
script = '''
|
||||
cargo clean
|
||||
'''
|
||||
|
||||
[tasks.clean-app]
|
||||
script = '''
|
||||
rm -rf packages/app/dist
|
||||
rm -rf packages/app/assets/wasm
|
||||
'''
|
||||
|
||||
[tasks.clean]
|
||||
dependencies = ["clean-server", "clean-app"]
|
||||
|
||||
[tasks.format]
|
||||
script = '''
|
||||
cargo +nightly fmt --all
|
||||
'''
|
||||
|
||||
[tasks.run]
|
||||
script = '''
|
||||
npm run app --workspace=packages/app
|
||||
'''
|
10
crates/src-collections/Cargo.toml
Normal file
10
crates/src-collections/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "src-collections"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = ["oksoftware"]
|
||||
|
||||
[dependencies]
|
||||
rustc-hash = "1.1.0"
|
||||
indexmap = "1.9.1"
|
||||
typed-index-collections = "3.0.3"
|
10
crates/src-collections/src/lib.rs
Normal file
10
crates/src-collections/src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use rustc_hash::FxHasher;
|
||||
use std::hash::BuildHasherDefault;
|
||||
|
||||
pub use rustc_hash::FxHashMap as Map;
|
||||
pub use rustc_hash::FxHashSet as Set;
|
||||
|
||||
pub type IndexMap<K, V> = indexmap::IndexMap<K, V, BuildHasherDefault<FxHasher>>;
|
||||
pub type IndexSet<V> = indexmap::IndexSet<V, BuildHasherDefault<FxHasher>>;
|
||||
|
||||
pub type IndexVec<K, V> = typed_index_collections::TiVec<K, V>;
|
30
crates/src-lsp-browser/Cargo.toml
Normal file
30
crates/src-lsp-browser/Cargo.toml
Normal file
|
@ -0,0 +1,30 @@
|
|||
[package]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
name = "src-lsp-browser"
|
||||
version = "0.0.0"
|
||||
|
||||
[features]
|
||||
default = ["tower-lsp/runtime-agnostic"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
src-lsp-server = { version = "0.0", path = "../src-lsp-server", default-features = false }
|
||||
futures = "0.3.21"
|
||||
js-sys = "0.3.57"
|
||||
tower-lsp = { version = "0.17.0", default-features = false }
|
||||
wasm-bindgen = "0.2.81"
|
||||
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
||||
wasm-streams = "0.2.3"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.57"
|
||||
features = [
|
||||
"console",
|
||||
"HtmlTextAreaElement",
|
||||
"ReadableStream",
|
||||
"WritableStream",
|
||||
]
|
62
crates/src-lsp-browser/src/lib.rs
Normal file
62
crates/src-lsp-browser/src/lib.rs
Normal file
|
@ -0,0 +1,62 @@
|
|||
#![deny(clippy::all)]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
use futures::stream::TryStreamExt;
|
||||
use tower_lsp::{LspService, Server};
|
||||
use wasm_bindgen::{prelude::*, JsCast};
|
||||
use wasm_bindgen_futures::stream::JsStream;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub struct ServerConfig {
|
||||
into_server: js_sys::AsyncIterator,
|
||||
from_server: web_sys::WritableStream,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
impl ServerConfig {
|
||||
#[wasm_bindgen(constructor)]
|
||||
pub fn new(into_server: js_sys::AsyncIterator, from_server: web_sys::WritableStream) -> Self {
|
||||
Self {
|
||||
into_server,
|
||||
from_server,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: we don't use web_sys::ReadableStream for input here because on the
|
||||
// browser side we need to use a ReadableByteStreamController to construct it
|
||||
// and so far only Chromium-based browsers support that functionality.
|
||||
|
||||
// NOTE: input needs to be an AsyncIterator<Uint8Array, never, void> specifically
|
||||
#[wasm_bindgen]
|
||||
pub async fn serve(config: ServerConfig) -> Result<(), JsValue> {
|
||||
console_error_panic_hook::set_once();
|
||||
|
||||
web_sys::console::log_1(&"server::serve".into());
|
||||
|
||||
let ServerConfig {
|
||||
into_server,
|
||||
from_server,
|
||||
} = config;
|
||||
|
||||
|
||||
let input = JsStream::from(into_server);
|
||||
let input = input
|
||||
.map_ok(|value| {
|
||||
value
|
||||
.dyn_into::<js_sys::Uint8Array>()
|
||||
.expect("could not cast stream item to Uint8Array")
|
||||
.to_vec()
|
||||
})
|
||||
.map_err(|_err| std::io::Error::from(std::io::ErrorKind::Other))
|
||||
.into_async_read();
|
||||
|
||||
let output = JsCast::unchecked_into::<wasm_streams::writable::sys::WritableStream>(from_server);
|
||||
let output = wasm_streams::WritableStream::from_raw(output);
|
||||
let output = output.try_into_async_write().map_err(|err| err.0)?;
|
||||
|
||||
let (service, messages) = LspService::new(|client| src_lsp_server::Server::new(client));
|
||||
Server::new(input, output, messages).serve(service).await;
|
||||
|
||||
Ok(())
|
||||
}
|
45
crates/src-lsp-server/Cargo.toml
Normal file
45
crates/src-lsp-server/Cargo.toml
Normal file
|
@ -0,0 +1,45 @@
|
|||
[package]
|
||||
publish = false
|
||||
edition = "2021"
|
||||
name = "src-lsp-server"
|
||||
version = "0.0.0"
|
||||
|
||||
[features]
|
||||
default = ["tower-lsp/runtime-agnostic"]
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.57"
|
||||
async-lock = "2.5.0"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
dashmap = "5.3.4"
|
||||
futures = "0.3.21"
|
||||
indoc = "1.0"
|
||||
js-sys = "0.3.57"
|
||||
log = "0.4"
|
||||
lsp = { version = "0.93", package = "lsp-types" }
|
||||
lsp-text = { version = "0.9"}
|
||||
ropey = "1.5.0"
|
||||
serde_json = "1.0"
|
||||
srclang = { version = "0.1.0", path = "../..", registry = "oksoftware" }
|
||||
salsa = { version = "0.1.0", registry = "oksoftware", package = "salsa-2022" }
|
||||
salsa-macros = { version = "0.1.0", registry = "oksoftware" , package = "salsa-2022-macros" }
|
||||
thiserror = "1.0"
|
||||
tower-lsp = { version = "0.17.0", default-features = false }
|
||||
wasm-bindgen = "0.2.81"
|
||||
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
||||
wasm-streams = "0.2.3"
|
||||
src-collections = { version = "0.1.0", path = "../src-collections", registry = "oksoftware" }
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.57"
|
||||
features = [
|
||||
"console",
|
||||
"CssStyleDeclaration",
|
||||
"Document",
|
||||
"ReadableStream",
|
||||
"Window",
|
||||
"WritableStream",
|
||||
]
|
10
crates/src-lsp-server/src/core.rs
Normal file
10
crates/src-lsp-server/src/core.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
pub mod document;
|
||||
pub mod error;
|
||||
pub mod session;
|
||||
pub mod syntax;
|
||||
pub mod text;
|
||||
|
||||
pub use document::*;
|
||||
pub use error::*;
|
||||
pub use session::*;
|
||||
pub use text::*;
|
66
crates/src-lsp-server/src/core/document.rs
Normal file
66
crates/src-lsp-server/src/core/document.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use async_lock::Mutex;
|
||||
use lsp_text::RopeExt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use srclang::{compiler::ir, Db};
|
||||
|
||||
|
||||
pub struct Document {
|
||||
pub content: ropey::Rope,
|
||||
pub db: &'static dyn Db,
|
||||
}
|
||||
|
||||
impl Document {
|
||||
pub async fn open(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DidOpenTextDocumentParams,
|
||||
) -> anyhow::Result<Option<Self>> {
|
||||
// let mut parser = crate::core::parser::javascript(&session.language)?;
|
||||
// let content = ropey::Rope::from(params.text_document.text);
|
||||
// let result = {
|
||||
// let content = content.clone();
|
||||
// let byte_idx = 0;
|
||||
// let callback = content.chunk_walker(byte_idx).callback_adapter_for_tree_sitter();
|
||||
// let old_tree = None;
|
||||
// parser.parse_with(callback, old_tree)?
|
||||
// };
|
||||
// crate::core::syntax::update_channel(result.as_ref());
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub async fn change<'changes>(
|
||||
session: Arc<crate::core::Session>,
|
||||
uri: &lsp::Url,
|
||||
content: &ropey::Rope,
|
||||
) -> anyhow::Result<Option<ir::Program>> {
|
||||
let result = {
|
||||
let parser = session.get_mut_parser(uri).await?;
|
||||
let mut parser = parser.lock().await;
|
||||
let text = content.chunks().collect::<String>();
|
||||
// parser.parse(text, None)?
|
||||
};
|
||||
// crate::core::syntax::update_channel(result.as_ref());
|
||||
// if let Some(tree) = result {
|
||||
// {
|
||||
// let tree = tree.clone();
|
||||
// *session.get_mut_tree(uri).await?.value_mut() = Mutex::new(tree);
|
||||
// }
|
||||
// Ok(Some(tree))
|
||||
// } else {
|
||||
Ok(None)
|
||||
// }
|
||||
}
|
||||
|
||||
/// Return the language-id and textual content portion of the [`Document`].
|
||||
pub fn text(&self) -> crate::core::Text {
|
||||
crate::core::Text {
|
||||
content: self.content.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub enum DocumentState {
|
||||
Closed,
|
||||
Opened,
|
||||
}
|
42
crates/src-lsp-server/src/core/error.rs
Normal file
42
crates/src-lsp-server/src/core/error.rs
Normal file
|
@ -0,0 +1,42 @@
|
|||
use crate::core;
|
||||
use thiserror::Error;
|
||||
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[derive(Debug, Error, PartialEq)]
|
||||
pub enum Error {
|
||||
#[error("ClientNotInitialzed")]
|
||||
ClientNotInitialized,
|
||||
#[error("core::SessionResourceNotFound: kind={kind:?}, uri={uri:?}")]
|
||||
SessionResourceNotFound {
|
||||
kind: core::session::SessionResourceKind,
|
||||
uri: lsp::Url,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct IntoJsonRpcError(pub anyhow::Error);
|
||||
|
||||
impl From<IntoJsonRpcError> for tower_lsp::jsonrpc::Error {
|
||||
fn from(error: IntoJsonRpcError) -> Self {
|
||||
let mut rpc_error = tower_lsp::jsonrpc::Error::internal_error();
|
||||
rpc_error.data = Some(serde_json::to_value(format!("{}", error.0)).unwrap());
|
||||
rpc_error
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{Error, IntoJsonRpcError};
|
||||
|
||||
#[test]
|
||||
fn from() {
|
||||
let error = Error::ClientNotInitialized;
|
||||
let error = error.into();
|
||||
|
||||
let mut expected = tower_lsp::jsonrpc::Error::internal_error();
|
||||
expected.data = Some(serde_json::to_value(format!("{}", error)).unwrap());
|
||||
|
||||
let actual: tower_lsp::jsonrpc::Error = IntoJsonRpcError(error).into();
|
||||
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
}
|
137
crates/src-lsp-server/src/core/session.rs
Normal file
137
crates/src-lsp-server/src/core/session.rs
Normal file
|
@ -0,0 +1,137 @@
|
|||
use anyhow::anyhow;
|
||||
use async_lock::{Mutex, RwLock};
|
||||
use dashmap::{
|
||||
mapref::one::{Ref, RefMut},
|
||||
DashMap,
|
||||
};
|
||||
use srclang::compiler::ir;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub enum SessionResourceKind {
|
||||
Document,
|
||||
Parser,
|
||||
Tree,
|
||||
}
|
||||
|
||||
pub struct Session {
|
||||
pub server_capabilities: RwLock<lsp::ServerCapabilities>,
|
||||
pub client_capabilities: RwLock<Option<lsp::ClientCapabilities>>,
|
||||
pub db: RwLock<Option<srclang::compiler::db::Database>>,
|
||||
client: Option<tower_lsp::Client>,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
pub fn new(client: Option<tower_lsp::Client>) -> Arc<Self> {
|
||||
let server_capabilities = RwLock::new(crate::server::capabilities());
|
||||
let client_capabilities = Default::default();
|
||||
Arc::new(Session {
|
||||
db: Default::default(),
|
||||
server_capabilities,
|
||||
client_capabilities,
|
||||
client,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn client(&self) -> anyhow::Result<&tower_lsp::Client> {
|
||||
self.client
|
||||
.as_ref()
|
||||
.ok_or_else(|| crate::core::Error::ClientNotInitialized.into())
|
||||
}
|
||||
|
||||
pub fn insert_document(&self, uri: lsp::Url, document: crate::core::Document) -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn remove_document(&self, uri: &lsp::Url) -> anyhow::Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn semantic_tokens_legend(&self) -> Option<lsp::SemanticTokensLegend> {
|
||||
let capabilities = self.server_capabilities.read().await;
|
||||
if let Some(capabilities) = &capabilities.semantic_tokens_provider {
|
||||
match capabilities {
|
||||
lsp::SemanticTokensServerCapabilities::SemanticTokensOptions(options) => Some(options.legend.clone()),
|
||||
lsp::SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(options) => {
|
||||
Some(options.semantic_tokens_options.legend.clone())
|
||||
},
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_text(&self, uri: &lsp::Url) -> anyhow::Result<Ref<'_, lsp::Url, crate::core::Text>> {
|
||||
Err({
|
||||
let kind = SessionResourceKind::Document;
|
||||
let uri = uri.clone();
|
||||
crate::core::Error::SessionResourceNotFound { kind, uri }.into()
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_mut_text(&self, uri: &lsp::Url) -> anyhow::Result<RefMut<'_, lsp::Url, crate::core::Text>> {
|
||||
Err({
|
||||
let kind = SessionResourceKind::Document;
|
||||
let uri = uri.clone();
|
||||
crate::core::Error::SessionResourceNotFound { kind, uri }.into()
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_mut_parser(
|
||||
&self,
|
||||
uri: &lsp::Url,
|
||||
) -> anyhow::Result<RefMut<'_, lsp::Url, Mutex<ir::Program>>> {
|
||||
Err({
|
||||
let kind = SessionResourceKind::Document;
|
||||
let uri = uri.clone();
|
||||
crate::core::Error::SessionResourceNotFound { kind, uri }.into()
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_tree(&self, uri: &lsp::Url) -> anyhow::Result<Ref<'_, lsp::Url, Mutex<ir::Program>>> {
|
||||
Err({
|
||||
let kind = SessionResourceKind::Document;
|
||||
let uri = uri.clone();
|
||||
crate::core::Error::SessionResourceNotFound { kind, uri }.into()
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_mut_tree(&self, uri: &lsp::Url) -> anyhow::Result<RefMut<'_, lsp::Url, Mutex<ir::Program>>> {
|
||||
Err({
|
||||
let kind = SessionResourceKind::Document;
|
||||
let uri = uri.clone();
|
||||
crate::core::Error::SessionResourceNotFound { kind, uri }.into()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_channel_syntax() -> anyhow::Result<web_sys::HtmlTextAreaElement> {
|
||||
use wasm_bindgen::JsCast;
|
||||
let element_id = "channel-syntax";
|
||||
let channel_syntax = web_sys::window()
|
||||
.ok_or_else(|| anyhow!("failed to get window"))?
|
||||
.document()
|
||||
.ok_or_else(|| anyhow!("failed to get document"))?
|
||||
.get_element_by_id(element_id)
|
||||
.ok_or_else(|| anyhow!("failed to get channel-syntax element"))?
|
||||
.unchecked_into();
|
||||
Ok(channel_syntax)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pub fn get_channel_syntax() -> anyhow::Result<web_sys::HtmlTextAreaElement> {
|
||||
use wasm_bindgen::JsCast;
|
||||
let element_id = "channel-syntax";
|
||||
let channel_syntax = web_sys::window()
|
||||
.ok_or_else(|| anyhow!("failed to get window"))?
|
||||
.document()
|
||||
.ok_or_else(|| anyhow!("failed to get document"))?
|
||||
.get_element_by_id(element_id)
|
||||
.ok_or_else(|| anyhow!("failed to get channel-syntax element"))?
|
||||
.unchecked_into();
|
||||
Ok(channel_syntax)
|
||||
}
|
15
crates/src-lsp-server/src/core/syntax.rs
Normal file
15
crates/src-lsp-server/src/core/syntax.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
use srclang::compiler::ir;
|
||||
|
||||
use crate::core::session::Session;
|
||||
|
||||
pub(crate) fn update_channel(tree: Option<&ir::Program>) {
|
||||
// assume errors; use red
|
||||
let mut color = "rgb(255, 87, 51)";
|
||||
if let Ok(channel_syntax) = Session::get_channel_syntax() {
|
||||
|
||||
channel_syntax
|
||||
.style()
|
||||
.set_property("background-color", color)
|
||||
.expect("failed to set style");
|
||||
}
|
||||
}
|
17
crates/src-lsp-server/src/core/text.rs
Normal file
17
crates/src-lsp-server/src/core/text.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
pub struct Text {
|
||||
pub content: ropey::Rope,
|
||||
}
|
||||
|
||||
impl Text {
|
||||
pub fn new(text: impl AsRef<str>) -> anyhow::Result<Self> {
|
||||
let text = text.as_ref();
|
||||
let content = ropey::Rope::from_str(text);
|
||||
Ok(Text { content })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::core::Document> for Text {
|
||||
fn from(value: crate::core::Document) -> Self {
|
||||
value.text()
|
||||
}
|
||||
}
|
46
crates/src-lsp-server/src/db.rs
Normal file
46
crates/src-lsp-server/src/db.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
use anyhow::Result;
|
||||
use lsp::{InitializeParams, InitializeResult, Url};
|
||||
use salsa::ParallelDatabase;
|
||||
use src_collections::Map;
|
||||
use srclang::compiler::text::SourceProgram;
|
||||
use tower_lsp::{jsonrpc, LanguageServer};
|
||||
|
||||
|
||||
pub struct LspServerDatabase {
|
||||
db: Mutex<srclang::compiler::db::Database>,
|
||||
input_files: Map<Url, SourceProgram>,
|
||||
}
|
||||
|
||||
impl LspServerDatabase {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
db: Mutex::new(srclang::compiler::db::Database::default()),
|
||||
input_files: Map::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db(&self) -> std::sync::MutexGuard<srclang::compiler::db::Database> {
|
||||
self.db.lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn input_files(&self) -> &Map<Url, SourceProgram> {
|
||||
&self.input_files
|
||||
}
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
impl LanguageServer for LspServerDatabase {
|
||||
async fn initialize(&self, params: InitializeParams) -> jsonrpc::Result<InitializeResult> {
|
||||
web_sys::console::log_1(&"server::initialize".into());
|
||||
// *self.session.client_capabilities.write().await = Some(params.capabilities);
|
||||
Ok(InitializeResult {
|
||||
..InitializeResult::default()
|
||||
})
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> jsonrpc::Result<()> {
|
||||
web_sys::console::log_1(&"server::shutdown".into());
|
||||
Ok(())
|
||||
}
|
||||
}
|
169
crates/src-lsp-server/src/handler.rs
Normal file
169
crates/src-lsp-server/src/handler.rs
Normal file
|
@ -0,0 +1,169 @@
|
|||
pub mod text_document {
|
||||
use std::sync::Arc;
|
||||
|
||||
use lsp_text::RopeExt;
|
||||
|
||||
pub async fn did_open(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DidOpenTextDocumentParams,
|
||||
) -> anyhow::Result<()> {
|
||||
let uri = params.text_document.uri.clone();
|
||||
|
||||
if let Some(document) = crate::core::Document::open(session.clone(), params).await? {
|
||||
session.insert_document(uri.clone(), document)?;
|
||||
} else {
|
||||
log::warn!("'textDocument/didOpen' failed :: uri: {:#?}", uri);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn did_change(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DidChangeTextDocumentParams,
|
||||
) -> anyhow::Result<()> {
|
||||
let uri = ¶ms.text_document.uri;
|
||||
let mut text = session.get_mut_text(uri).await?;
|
||||
*text = crate::core::Text::new(params.content_changes[0].text.clone())?;
|
||||
crate::core::Document::change(session.clone(), uri, &text.content).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn did_close(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DidCloseTextDocumentParams,
|
||||
) -> anyhow::Result<()> {
|
||||
let uri = params.text_document.uri;
|
||||
session.remove_document(&uri)?;
|
||||
let diagnostics = Default::default();
|
||||
let version = Default::default();
|
||||
session.client()?.publish_diagnostics(uri, diagnostics, version).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn document_symbol(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DocumentSymbolParams,
|
||||
) -> anyhow::Result<Option<lsp::DocumentSymbolResponse>> {
|
||||
// use wasm_bindgen::JsCast;
|
||||
|
||||
// fn make_symbol(
|
||||
// uri: &lsp::Url,
|
||||
// content: &ropey::Rope,
|
||||
// declaration: tree_sitter::Node,
|
||||
// identifier: tree_sitter::Node,
|
||||
// kind: lsp::SymbolKind,
|
||||
// ) -> lsp::SymbolInformation {
|
||||
// let name = content.utf8_text_for_tree_sitter_node(&identifier).into();
|
||||
// let range = content.tree_sitter_range_to_lsp_range(declaration.range());
|
||||
// #[allow(deprecated)]
|
||||
// lsp::SymbolInformation {
|
||||
// name,
|
||||
// kind,
|
||||
// tags: Default::default(),
|
||||
// deprecated: Default::default(),
|
||||
// location: lsp::Location::new(uri.clone(), range),
|
||||
// container_name: Default::default(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// let uri = ¶ms.text_document.uri;
|
||||
|
||||
// let text = session.get_text(uri).await?;
|
||||
// let content = &text.content;
|
||||
|
||||
// let tree = session.get_tree(uri).await?;
|
||||
// let tree = tree.lock().await.clone();
|
||||
|
||||
// // NOTE: transmutes here because we do not yet support query functionality in
|
||||
// // tree-sitter-facade; thus we use the raw bindings from web-tree-sitter-sys.
|
||||
|
||||
// #[allow(unsafe_code)]
|
||||
// // let node = unsafe { std::mem::transmute::<_, web_tree_sitter_sys::SyntaxNode>(tree.root_node()) };
|
||||
// #[allow(unsafe_code)]
|
||||
// // let language = unsafe { std::mem::transmute::<_, web_tree_sitter_sys::Language>(session.language.clone()) };
|
||||
|
||||
// static QUERY: &str = indoc::indoc! {r"
|
||||
// (function_declaration
|
||||
// name: (identifier) @identifier) @function_declaration
|
||||
// (lexical_declaration
|
||||
// (variable_declarator
|
||||
// name: (identifier) @identifier)) @class_declaration
|
||||
// (variable_declaration
|
||||
// (variable_declarator
|
||||
// name: (identifier) @identifier)) @variable_declaration
|
||||
// (class_declaration
|
||||
// name: (identifier) @identifier) @class_declaration
|
||||
// "};
|
||||
// let query = language.query(&QUERY.into()).expect("failed to create query");
|
||||
// let matches = {
|
||||
// let start_position = None;
|
||||
// let end_position = None;
|
||||
// query
|
||||
// .matches(&node, start_position, end_position)
|
||||
// .into_vec()
|
||||
// .into_iter()
|
||||
// .map(JsCast::unchecked_into::<web_tree_sitter_sys::QueryMatch>)
|
||||
// };
|
||||
|
||||
// let mut symbols = vec![];
|
||||
|
||||
// for r#match in matches {
|
||||
// let captures = r#match
|
||||
// .captures()
|
||||
// .into_vec()
|
||||
// .into_iter()
|
||||
// .map(JsCast::unchecked_into::<web_tree_sitter_sys::QueryCapture>)
|
||||
// .collect::<Vec<_>>();
|
||||
// if let [declaration, identifier] = captures.as_slice() {
|
||||
// // NOTE: reverse the transmutes from above so we can use tree-sitter-facade bindings for Node
|
||||
// #[allow(unsafe_code)]
|
||||
// // let declaration_node = unsafe { std::mem::transmute::<_, tree_sitter::Node>(declaration.node()) };
|
||||
// #[allow(unsafe_code)]
|
||||
// // let identifier_node = unsafe { std::mem::transmute::<_, tree_sitter::Node>(identifier.node()) };
|
||||
// match String::from(declaration.name()).as_str() {
|
||||
// "function_declaration" => {
|
||||
// symbols.push(make_symbol(
|
||||
// uri,
|
||||
// content,
|
||||
// declaration_node,
|
||||
// identifier_node,
|
||||
// lsp::SymbolKind::FUNCTION,
|
||||
// ));
|
||||
// },
|
||||
// "lexical_declaration" => {
|
||||
// symbols.push(make_symbol(
|
||||
// uri,
|
||||
// content,
|
||||
// declaration_node,
|
||||
// identifier_node,
|
||||
// lsp::SymbolKind::VARIABLE,
|
||||
// ));
|
||||
// },
|
||||
// "variable_declaration" => {
|
||||
// symbols.push(make_symbol(
|
||||
// uri,
|
||||
// content,
|
||||
// declaration_node,
|
||||
// identifier_node,
|
||||
// lsp::SymbolKind::VARIABLE,
|
||||
// ));
|
||||
// },
|
||||
// "class_declaration" => {
|
||||
// symbols.push(make_symbol(
|
||||
// uri,
|
||||
// content,
|
||||
// declaration_node,
|
||||
// identifier_node,
|
||||
// lsp::SymbolKind::VARIABLE,
|
||||
// ));
|
||||
// },
|
||||
// _ => {},
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Ok(Some(lsp::DocumentSymbolResponse::Flat(symbols)))
|
||||
Ok(None)
|
||||
}
|
||||
}
|
10
crates/src-lsp-server/src/lib.rs
Normal file
10
crates/src-lsp-server/src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#![deny(clippy::all)]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
// mod core;
|
||||
// pub mod handler;
|
||||
mod server;
|
||||
|
||||
mod db;
|
||||
|
||||
pub use server::*;
|
139
crates/src-lsp-server/src/server.rs
Normal file
139
crates/src-lsp-server/src/server.rs
Normal file
|
@ -0,0 +1,139 @@
|
|||
use anyhow::anyhow;
|
||||
use std::result::Result::Ok;
|
||||
use std::sync::Arc;
|
||||
use tower_lsp::{jsonrpc, lsp_types::*, LanguageServer};
|
||||
|
||||
pub fn capabilities() -> lsp::ServerCapabilities {
|
||||
let document_symbol_provider = Some(lsp::OneOf::Left(true));
|
||||
|
||||
let text_document_sync = {
|
||||
let options = lsp::TextDocumentSyncOptions {
|
||||
open_close: Some(true),
|
||||
change: Some(lsp::TextDocumentSyncKind::FULL),
|
||||
..Default::default()
|
||||
};
|
||||
Some(lsp::TextDocumentSyncCapability::Options(options))
|
||||
};
|
||||
|
||||
let hover_provider = Some(lsp::HoverProviderCapability::Simple(true));
|
||||
|
||||
lsp::ServerCapabilities {
|
||||
text_document_sync,
|
||||
document_symbol_provider,
|
||||
hover_provider,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Server {
|
||||
pub client: tower_lsp::Client,
|
||||
pub db: Arc<crate::db::LspServerDatabase>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(client: tower_lsp::Client) -> Self {
|
||||
// let session = crate::core::Session::new(Some(client.clone()), language);
|
||||
Server {
|
||||
client,
|
||||
db: Arc::new(crate::db::LspServerDatabase::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tower_lsp::async_trait]
|
||||
impl LanguageServer for Server {
|
||||
async fn initialize(&self, params: InitializeParams) -> jsonrpc::Result<InitializeResult> {
|
||||
web_sys::console::log_1(&"server::initialize".into());
|
||||
// *self.session.client_capabilities.write().await = Some(params.capabilities);
|
||||
let capabilities = capabilities();
|
||||
Ok(InitializeResult {
|
||||
capabilities,
|
||||
..InitializeResult::default()
|
||||
})
|
||||
}
|
||||
|
||||
async fn initialized(&self, _: lsp::InitializedParams) {
|
||||
web_sys::console::log_1(&"server::initialized".into());
|
||||
let typ = lsp::MessageType::INFO;
|
||||
let message = "src language server initialized!";
|
||||
self.client.log_message(typ, message).await;
|
||||
}
|
||||
|
||||
async fn shutdown(&self) -> jsonrpc::Result<()> {
|
||||
web_sys::console::log_1(&"server::shutdown".into());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// FIXME: for some reason this doesn't trigger
|
||||
async fn did_open(&self, params: lsp::DidOpenTextDocumentParams) {
|
||||
web_sys::console::log_1(&"server::did_open".into());
|
||||
|
||||
let typ = lsp::MessageType::INFO;
|
||||
let message = format!("opened document: {}", params.text_document.uri.as_str());
|
||||
self.client.log_message(typ, message).await;
|
||||
let mut errors = vec![];
|
||||
let wrapper = srclang::lexer::TripleIterator::new(¶ms.text_document.text);
|
||||
let t = srclang::parser::src::SourceParser::new()
|
||||
.parse(&mut errors, wrapper)
|
||||
.unwrap();
|
||||
let mut color = "rgb(255, 87, 51)";
|
||||
if let Ok(channel_syntax) = get_channel_syntax() {
|
||||
channel_syntax.set_value(format!("{:#?}", t).as_str());
|
||||
if !errors.is_empty() {
|
||||
channel_syntax
|
||||
.style()
|
||||
.set_property("background-color", color)
|
||||
.expect("failed to set style");
|
||||
}
|
||||
}
|
||||
|
||||
web_sys::console::log_1(&"server::did parse".into());
|
||||
self.client
|
||||
.log_message(typ, format!("{:#?}", params.text_document.text))
|
||||
.await;
|
||||
// let session = self.session.clone();
|
||||
// crate::handler::text_document::did_open(session, params).await.unwrap();
|
||||
}
|
||||
|
||||
async fn did_change(&self, params: lsp::DidChangeTextDocumentParams) {
|
||||
web_sys::console::log_1(&"server::did_change".into());
|
||||
let typ = lsp::MessageType::INFO;
|
||||
let mut errors = vec![];
|
||||
let wrapper = srclang::lexer::TripleIterator::new("");
|
||||
|
||||
let t = srclang::parser::src::SourceParser::new().parse(&mut errors, wrapper);
|
||||
if errors.is_empty() {
|
||||
for change in params.content_changes {
|
||||
self.client
|
||||
.log_message(typ, format!("{:#?}", change.text))
|
||||
.await;
|
||||
}
|
||||
} else {
|
||||
self.client.log_message(typ, format!("{:#?}", errors)).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn document_symbol(
|
||||
&self,
|
||||
params: lsp::DocumentSymbolParams,
|
||||
) -> jsonrpc::Result<Option<lsp::DocumentSymbolResponse>> {
|
||||
web_sys::console::log_1(&"server::document_symbol".into());
|
||||
// let session = self.session.clone();
|
||||
// let result = crate::handler::text_document::document_symbol(session, params).await;
|
||||
// Ok(result.map_err(crate::core::IntoJsonRpcError)?)
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_channel_syntax() -> anyhow::Result<web_sys::HtmlTextAreaElement> {
|
||||
use wasm_bindgen::JsCast;
|
||||
let element_id = "channel-syntax";
|
||||
let channel_syntax = web_sys::window()
|
||||
.ok_or_else(|| anyhow!("failed to get window"))?
|
||||
.document()
|
||||
.ok_or_else(|| anyhow!("failed to get document"))?
|
||||
.get_element_by_id(element_id)
|
||||
.ok_or_else(|| anyhow!("failed to get channel-syntax element"))?
|
||||
.unchecked_into();
|
||||
Ok(channel_syntax)
|
||||
}
|
|
@ -41,3 +41,11 @@ impl Make for Local {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
Building upon the incredible work of the Rust community, src would not be possible without the following projects:
|
||||
|
||||
- [salsa-rs](https://github.com/salsa-rs/salsa)
|
||||
- [lalrpop](https://github.com/lalrpop/lalrpop)
|
||||
- [tower-lsp-web-demo](https://github.com/silvanshade/tower-lsp-web-demo)
|
|
@ -1,5 +1,13 @@
|
|||
# Summary
|
||||
|
||||
- [Intro](0intro.md)
|
||||
|
||||
|
||||
# Language
|
||||
- [Specification](language/0intro.md)
|
||||
- [Examples](examples.md)
|
||||
- [Language](language/0intro.md)
|
||||
|
||||
|
||||
# Playground
|
||||
|
||||
* [Playground](playground/index.md)
|
22
docs/playground/index.md
Normal file
22
docs/playground/index.md
Normal file
|
@ -0,0 +1,22 @@
|
|||
<div id="container">
|
||||
<div id="cell-editor">
|
||||
<label for="editor">editor</label>
|
||||
<div id="editor"></div>
|
||||
</div>
|
||||
<div id="cell-syntax">
|
||||
<label for="channel-syntax">syntax</label>
|
||||
<textarea id="channel-syntax" autocomplete="off" spellcheck="off" wrap="off" readonly></textarea>
|
||||
</div>
|
||||
<div id="cell-client">
|
||||
<label for="channel-client">message trace (client ⇒ server)</label>
|
||||
<textarea id="channel-client" autocomplete="off" spellcheck="off" wrap="off" readonly rows="4"></textarea>
|
||||
</div>
|
||||
<div id="cell-server">
|
||||
<label for="channel-server">message trace (client ⇐ server)</label>
|
||||
<textarea id="channel-server" autocomplete="off" spellcheck="off" wrap="off" readonly rows="4"></textarea>
|
||||
</div>
|
||||
<div id="cell-console">
|
||||
<label for="channel-console">console</label>
|
||||
<textarea id="channel-console" autocomplete="off" spellcheck="off" wrap="off" readonly rows="3"></textarea>
|
||||
</div>
|
||||
</div>
|
BIN
docs/playground/taocp.png
Normal file
BIN
docs/playground/taocp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 822 KiB |
3
docs/theme/index.hbs
vendored
3
docs/theme/index.hbs
vendored
|
@ -24,7 +24,6 @@
|
|||
<link rel="stylesheet" href="{{ path_to_root }}css/variables.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/general.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/chrome.css">
|
||||
<link rel="stylesheet" href="{{ path_to_root }}ok.css"
|
||||
{{#if print_enable}}
|
||||
<link rel="stylesheet" href="{{ path_to_root }}css/print.css" media="print">
|
||||
{{/if}}
|
||||
|
@ -150,7 +149,7 @@
|
|||
🍔
|
||||
</label>
|
||||
<button id="theme-toggle" class="nes-btn" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
||||
🎨
|
||||
💅
|
||||
</button>
|
||||
<ul id="theme-list" class="is-primary theme-popup" aria-label="Themes" role="menu">
|
||||
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
|
||||
|
|
4
ok.css
4
ok.css
|
@ -2940,6 +2940,10 @@ pre {
|
|||
padding: 16px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
label {
|
||||
font-family: "Press Start 2P", cursive !important;
|
||||
font-size: x-small !important;
|
||||
}
|
||||
nav,
|
||||
pre
|
||||
code {
|
||||
|
|
6502
package-lock.json
generated
Normal file
6502
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
10
package.json
Normal file
10
package.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/app"
|
||||
],
|
||||
"dependencies": {
|
||||
"tree-sitter-javascript": "^0.19.0",
|
||||
"web-tree-sitter-wasm-bindgen": "silvanshade/web-tree-sitter-wasm-bindgen"
|
||||
}
|
||||
}
|
1
packages/app/.prettierignore
Normal file
1
packages/app/.prettierignore
Normal file
|
@ -0,0 +1 @@
|
|||
dist
|
51
packages/app/assets/index.html
Normal file
51
packages/app/assets/index.html
Normal file
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>
|
||||
<%= htmlWebpackPlugin.options.title %>
|
||||
</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="container">
|
||||
<h1 id="title">browser-hosted editor and language server</h1>
|
||||
<p id="synopsis">
|
||||
This app demos an editor with language smarts (for JavaScript) by hosting the <a
|
||||
href="https://microsoft.github.io/monaco-editor/">Monaco</a> widget with an <a
|
||||
href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/">LSP</a>
|
||||
server implemented via <a href="https://github.com/ebkalderon/tower-lsp">tower-lsp</a> and <a
|
||||
href="https://github.com/tree-sitter/tree-sitter">tree-sitter</a>. Everything is compiled to WASM and run
|
||||
client-side directly in the browser; there are no separate sever-side processes or web workers used by the app
|
||||
code. (Monaco itself does use web workers, however).
|
||||
</p>
|
||||
<p id="features">
|
||||
<strong>features</strong>: ⇧⌘O (macos) or ⇧⌃O (windows) opens symbol view; the <strong>syntax</strong> area shows
|
||||
the JavaScript syntax tree (green for valid; red for errors) parsed from <strong>editor</strong>
|
||||
</p>
|
||||
<div id="cell-editor">
|
||||
<label for="editor">editor</label>
|
||||
<div id="editor"></div>
|
||||
</div>
|
||||
<div id="cell-syntax">
|
||||
<label for="channel-syntax">syntax</label>
|
||||
<textarea id="channel-syntax" autocomplete="off" spellcheck="off" wrap="off" readonly></textarea>
|
||||
</div>
|
||||
<div id="cell-console">
|
||||
<label for="channel-console">console</label>
|
||||
<textarea id="channel-console" autocomplete="off" spellcheck="off" wrap="off" readonly rows="3"></textarea>
|
||||
</div>
|
||||
<div id="cell-client">
|
||||
<label for="channel-client">message trace (client ⇒ server)</label>
|
||||
<textarea id="channel-client" autocomplete="off" spellcheck="off" wrap="off" readonly rows="4"></textarea>
|
||||
</div>
|
||||
<div id="cell-server">
|
||||
<label for="channel-server">message trace (client ⇐ server)</label>
|
||||
<textarea id="channel-server" autocomplete="off" spellcheck="off" wrap="off" readonly rows="4"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
116
packages/app/assets/index.module.css
Normal file
116
packages/app/assets/index.module.css
Normal file
|
@ -0,0 +1,116 @@
|
|||
a {
|
||||
color: mediumslateblue;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: silver;
|
||||
}
|
||||
/*
|
||||
body {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
background-color: black;
|
||||
color: white;
|
||||
} */
|
||||
|
||||
div[id=container] {
|
||||
display: grid;
|
||||
|
||||
height: 90%;
|
||||
|
||||
min-height: 600px;
|
||||
grid-template-rows: auto auto auto minmax(0, 1fr) auto auto auto;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin: auto;
|
||||
}
|
||||
/*
|
||||
h1[id=title] {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 1;
|
||||
font-family: monospace;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
p[id=synopsis] {
|
||||
color: lightgrey;
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 2;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5em;
|
||||
}
|
||||
|
||||
p[id=features] {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 3;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
font-style: italic;
|
||||
font-size: 10pt;
|
||||
line-height: 1.5em;
|
||||
color: lightgrey;
|
||||
}
|
||||
|
||||
div[id=cell-editor] {
|
||||
grid-column: 1 / 2;
|
||||
grid-row: 4;
|
||||
} */
|
||||
|
||||
[id=cell-editor] div[id=editor] {
|
||||
border: 1px solid black;
|
||||
height: calc(100vh - 500px);
|
||||
padding-right: 1.5em;
|
||||
}
|
||||
/*
|
||||
div[id=cell-syntax] {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 4;
|
||||
} */
|
||||
|
||||
div[id=cell-syntax] textarea {
|
||||
height: calc(100vh - 500px);
|
||||
}
|
||||
/*
|
||||
[id=container] label {
|
||||
display: block;
|
||||
font-family: monospace;
|
||||
font-size: 14pt;
|
||||
}
|
||||
|
||||
div[id=cell-console] {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 5;
|
||||
margin-top: 2em;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div[id=cell-client] {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 6;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div[id=cell-server] {
|
||||
grid-column: 1 / 3;
|
||||
grid-row: 7;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div[id=container] textarea {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
resize: none;
|
||||
overflow-y: auto;
|
||||
font-family: monospace;
|
||||
font-family: 10pt;
|
||||
} */
|
||||
main {
|
||||
max-width: 100%;
|
||||
}
|
||||
.content main {
|
||||
margin-inline-start: auto;
|
||||
margin-inline-end: auto;
|
||||
max-width: 100%;
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export function bytes_literal() { return "bytes"; }
|
164
packages/app/assets/wasm/src_lsp_browser.d.ts
vendored
Normal file
164
packages/app/assets/wasm/src_lsp_browser.d.ts
vendored
Normal file
|
@ -0,0 +1,164 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* @param {ServerConfig} config
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function serve(config: ServerConfig): Promise<void>;
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingByteSource {
|
||||
free(): void;
|
||||
/**
|
||||
* @param {any} controller
|
||||
*/
|
||||
start(controller: any): void;
|
||||
/**
|
||||
* @param {any} controller
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
pull(controller: any): Promise<any>;
|
||||
/**
|
||||
*/
|
||||
cancel(): void;
|
||||
/**
|
||||
*/
|
||||
readonly autoAllocateChunkSize: number;
|
||||
/**
|
||||
*/
|
||||
readonly type: any;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingSink {
|
||||
free(): void;
|
||||
/**
|
||||
* @param {any} chunk
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
write(chunk: any): Promise<any>;
|
||||
/**
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
close(): Promise<any>;
|
||||
/**
|
||||
* @param {any} reason
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
abort(reason: any): Promise<any>;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingSource {
|
||||
free(): void;
|
||||
/**
|
||||
* @param {any} controller
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
pull(controller: any): Promise<any>;
|
||||
/**
|
||||
*/
|
||||
cancel(): void;
|
||||
}
|
||||
/**
|
||||
* Raw options for [`pipeTo()`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeTo).
|
||||
*/
|
||||
export class PipeOptions {
|
||||
free(): void;
|
||||
/**
|
||||
*/
|
||||
readonly preventAbort: boolean;
|
||||
/**
|
||||
*/
|
||||
readonly preventCancel: boolean;
|
||||
/**
|
||||
*/
|
||||
readonly preventClose: boolean;
|
||||
/**
|
||||
*/
|
||||
readonly signal: AbortSignal | undefined;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class QueuingStrategy {
|
||||
free(): void;
|
||||
/**
|
||||
*/
|
||||
readonly highWaterMark: number;
|
||||
}
|
||||
/**
|
||||
* Raw options for [`getReader()`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/getReader).
|
||||
*/
|
||||
export class ReadableStreamGetReaderOptions {
|
||||
free(): void;
|
||||
/**
|
||||
*/
|
||||
readonly mode: any;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class ServerConfig {
|
||||
free(): void;
|
||||
/**
|
||||
* @param {AsyncIterator<any>} into_server
|
||||
* @param {WritableStream} from_server
|
||||
*/
|
||||
constructor(into_server: AsyncIterator<any>, from_server: WritableStream);
|
||||
}
|
||||
|
||||
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
|
||||
|
||||
export interface InitOutput {
|
||||
readonly memory: WebAssembly.Memory;
|
||||
readonly __wbg_serverconfig_free: (a: number) => void;
|
||||
readonly serve: (a: number) => number;
|
||||
readonly serverconfig_new: (a: number, b: number) => number;
|
||||
readonly __wbg_queuingstrategy_free: (a: number) => void;
|
||||
readonly queuingstrategy_highWaterMark: (a: number) => number;
|
||||
readonly __wbg_readablestreamgetreaderoptions_free: (a: number) => void;
|
||||
readonly readablestreamgetreaderoptions_mode: (a: number) => number;
|
||||
readonly __wbg_pipeoptions_free: (a: number) => void;
|
||||
readonly pipeoptions_preventClose: (a: number) => number;
|
||||
readonly pipeoptions_preventCancel: (a: number) => number;
|
||||
readonly pipeoptions_preventAbort: (a: number) => number;
|
||||
readonly pipeoptions_signal: (a: number) => number;
|
||||
readonly __wbg_intounderlyingbytesource_free: (a: number) => void;
|
||||
readonly intounderlyingbytesource_type: (a: number) => number;
|
||||
readonly intounderlyingbytesource_autoAllocateChunkSize: (a: number) => number;
|
||||
readonly intounderlyingbytesource_start: (a: number, b: number) => void;
|
||||
readonly intounderlyingbytesource_pull: (a: number, b: number) => number;
|
||||
readonly intounderlyingbytesource_cancel: (a: number) => void;
|
||||
readonly __wbg_intounderlyingsource_free: (a: number) => void;
|
||||
readonly intounderlyingsource_pull: (a: number, b: number) => number;
|
||||
readonly intounderlyingsource_cancel: (a: number) => void;
|
||||
readonly __wbg_intounderlyingsink_free: (a: number) => void;
|
||||
readonly intounderlyingsink_write: (a: number, b: number) => number;
|
||||
readonly intounderlyingsink_close: (a: number) => number;
|
||||
readonly intounderlyingsink_abort: (a: number, b: number) => number;
|
||||
readonly __wbindgen_malloc: (a: number) => number;
|
||||
readonly __wbindgen_realloc: (a: number, b: number, c: number) => number;
|
||||
readonly __wbindgen_export_2: WebAssembly.Table;
|
||||
readonly _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha2071623bfe400a9: (a: number, b: number, c: number) => void;
|
||||
readonly __wbindgen_exn_store: (a: number) => void;
|
||||
readonly __wbindgen_free: (a: number, b: number) => void;
|
||||
readonly wasm_bindgen__convert__closures__invoke2_mut__h18cbcd62478d99de: (a: number, b: number, c: number, d: number) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously compiles the given `bytes` and instantiates the WebAssembly module.
|
||||
*
|
||||
* @param {BufferSource} bytes
|
||||
*
|
||||
* @returns {InitOutput}
|
||||
*/
|
||||
export function initSync(bytes: BufferSource): InitOutput;
|
||||
|
||||
/**
|
||||
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
|
||||
* for everything else, calls `WebAssembly.instantiate` directly.
|
||||
*
|
||||
* @param {InitInput | Promise<InitInput>} module_or_path
|
||||
*
|
||||
* @returns {Promise<InitOutput>}
|
||||
*/
|
||||
export default function init (module_or_path?: InitInput | Promise<InitInput>): Promise<InitOutput>;
|
811
packages/app/assets/wasm/src_lsp_browser.js
Normal file
811
packages/app/assets/wasm/src_lsp_browser.js
Normal file
|
@ -0,0 +1,811 @@
|
|||
import { bytes_literal } from './snippets/wasm-streams-42e57edbcd526312/inline0.js';
|
||||
|
||||
let wasm;
|
||||
|
||||
const heap = new Array(32).fill(undefined);
|
||||
|
||||
heap.push(undefined, null, true, false);
|
||||
|
||||
function getObject(idx) { return heap[idx]; }
|
||||
|
||||
let heap_next = heap.length;
|
||||
|
||||
function dropObject(idx) {
|
||||
if (idx < 36) return;
|
||||
heap[idx] = heap_next;
|
||||
heap_next = idx;
|
||||
}
|
||||
|
||||
function takeObject(idx) {
|
||||
const ret = getObject(idx);
|
||||
dropObject(idx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||
|
||||
cachedTextDecoder.decode();
|
||||
|
||||
let cachedUint8Memory0;
|
||||
function getUint8Memory0() {
|
||||
if (cachedUint8Memory0.byteLength === 0) {
|
||||
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedUint8Memory0;
|
||||
}
|
||||
|
||||
function getStringFromWasm0(ptr, len) {
|
||||
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||
}
|
||||
|
||||
function addHeapObject(obj) {
|
||||
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||
const idx = heap_next;
|
||||
heap_next = heap[idx];
|
||||
|
||||
heap[idx] = obj;
|
||||
return idx;
|
||||
}
|
||||
|
||||
let WASM_VECTOR_LEN = 0;
|
||||
|
||||
const cachedTextEncoder = new TextEncoder('utf-8');
|
||||
|
||||
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
|
||||
? function (arg, view) {
|
||||
return cachedTextEncoder.encodeInto(arg, view);
|
||||
}
|
||||
: function (arg, view) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
view.set(buf);
|
||||
return {
|
||||
read: arg.length,
|
||||
written: buf.length
|
||||
};
|
||||
});
|
||||
|
||||
function passStringToWasm0(arg, malloc, realloc) {
|
||||
|
||||
if (realloc === undefined) {
|
||||
const buf = cachedTextEncoder.encode(arg);
|
||||
const ptr = malloc(buf.length);
|
||||
getUint8Memory0().subarray(ptr, ptr + buf.length).set(buf);
|
||||
WASM_VECTOR_LEN = buf.length;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
let len = arg.length;
|
||||
let ptr = malloc(len);
|
||||
|
||||
const mem = getUint8Memory0();
|
||||
|
||||
let offset = 0;
|
||||
|
||||
for (; offset < len; offset++) {
|
||||
const code = arg.charCodeAt(offset);
|
||||
if (code > 0x7F) break;
|
||||
mem[ptr + offset] = code;
|
||||
}
|
||||
|
||||
if (offset !== len) {
|
||||
if (offset !== 0) {
|
||||
arg = arg.slice(offset);
|
||||
}
|
||||
ptr = realloc(ptr, len, len = offset + arg.length * 3);
|
||||
const view = getUint8Memory0().subarray(ptr + offset, ptr + len);
|
||||
const ret = encodeString(arg, view);
|
||||
|
||||
offset += ret.written;
|
||||
}
|
||||
|
||||
WASM_VECTOR_LEN = offset;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
function isLikeNone(x) {
|
||||
return x === undefined || x === null;
|
||||
}
|
||||
|
||||
let cachedInt32Memory0;
|
||||
function getInt32Memory0() {
|
||||
if (cachedInt32Memory0.byteLength === 0) {
|
||||
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
}
|
||||
return cachedInt32Memory0;
|
||||
}
|
||||
|
||||
function debugString(val) {
|
||||
// primitive types
|
||||
const type = typeof val;
|
||||
if (type == 'number' || type == 'boolean' || val == null) {
|
||||
return `${val}`;
|
||||
}
|
||||
if (type == 'string') {
|
||||
return `"${val}"`;
|
||||
}
|
||||
if (type == 'symbol') {
|
||||
const description = val.description;
|
||||
if (description == null) {
|
||||
return 'Symbol';
|
||||
} else {
|
||||
return `Symbol(${description})`;
|
||||
}
|
||||
}
|
||||
if (type == 'function') {
|
||||
const name = val.name;
|
||||
if (typeof name == 'string' && name.length > 0) {
|
||||
return `Function(${name})`;
|
||||
} else {
|
||||
return 'Function';
|
||||
}
|
||||
}
|
||||
// objects
|
||||
if (Array.isArray(val)) {
|
||||
const length = val.length;
|
||||
let debug = '[';
|
||||
if (length > 0) {
|
||||
debug += debugString(val[0]);
|
||||
}
|
||||
for(let i = 1; i < length; i++) {
|
||||
debug += ', ' + debugString(val[i]);
|
||||
}
|
||||
debug += ']';
|
||||
return debug;
|
||||
}
|
||||
// Test for built-in
|
||||
const builtInMatches = /\[object ([^\]]+)\]/.exec(toString.call(val));
|
||||
let className;
|
||||
if (builtInMatches.length > 1) {
|
||||
className = builtInMatches[1];
|
||||
} else {
|
||||
// Failed to match the standard '[object ClassName]'
|
||||
return toString.call(val);
|
||||
}
|
||||
if (className == 'Object') {
|
||||
// we're a user defined class or Object
|
||||
// JSON.stringify avoids problems with cycles, and is generally much
|
||||
// easier than looping through ownProperties of `val`.
|
||||
try {
|
||||
return 'Object(' + JSON.stringify(val) + ')';
|
||||
} catch (_) {
|
||||
return 'Object';
|
||||
}
|
||||
}
|
||||
// errors
|
||||
if (val instanceof Error) {
|
||||
return `${val.name}: ${val.message}\n${val.stack}`;
|
||||
}
|
||||
// TODO we could test for more things here, like `Set`s and `Map`s.
|
||||
return className;
|
||||
}
|
||||
|
||||
function makeMutClosure(arg0, arg1, dtor, f) {
|
||||
const state = { a: arg0, b: arg1, cnt: 1, dtor };
|
||||
const real = (...args) => {
|
||||
// First up with a closure we increment the internal reference
|
||||
// count. This ensures that the Rust closure environment won't
|
||||
// be deallocated while we're invoking it.
|
||||
state.cnt++;
|
||||
const a = state.a;
|
||||
state.a = 0;
|
||||
try {
|
||||
return f(a, state.b, ...args);
|
||||
} finally {
|
||||
if (--state.cnt === 0) {
|
||||
wasm.__wbindgen_export_2.get(state.dtor)(a, state.b);
|
||||
|
||||
} else {
|
||||
state.a = a;
|
||||
}
|
||||
}
|
||||
};
|
||||
real.original = state;
|
||||
|
||||
return real;
|
||||
}
|
||||
function __wbg_adapter_22(arg0, arg1, arg2) {
|
||||
wasm._dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha2071623bfe400a9(arg0, arg1, addHeapObject(arg2));
|
||||
}
|
||||
|
||||
function _assertClass(instance, klass) {
|
||||
if (!(instance instanceof klass)) {
|
||||
throw new Error(`expected instance of ${klass.name}`);
|
||||
}
|
||||
return instance.ptr;
|
||||
}
|
||||
/**
|
||||
* @param {ServerConfig} config
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export function serve(config) {
|
||||
_assertClass(config, ServerConfig);
|
||||
var ptr0 = config.ptr;
|
||||
config.ptr = 0;
|
||||
const ret = wasm.serve(ptr0);
|
||||
return takeObject(ret);
|
||||
}
|
||||
|
||||
function handleError(f, args) {
|
||||
try {
|
||||
return f.apply(this, args);
|
||||
} catch (e) {
|
||||
wasm.__wbindgen_exn_store(addHeapObject(e));
|
||||
}
|
||||
}
|
||||
function __wbg_adapter_107(arg0, arg1, arg2, arg3) {
|
||||
wasm.wasm_bindgen__convert__closures__invoke2_mut__h18cbcd62478d99de(arg0, arg1, addHeapObject(arg2), addHeapObject(arg3));
|
||||
}
|
||||
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingByteSource {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_intounderlyingbytesource_free(ptr);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get type() {
|
||||
const ret = wasm.intounderlyingbytesource_type(this.ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get autoAllocateChunkSize() {
|
||||
const ret = wasm.intounderlyingbytesource_autoAllocateChunkSize(this.ptr);
|
||||
return ret >>> 0;
|
||||
}
|
||||
/**
|
||||
* @param {any} controller
|
||||
*/
|
||||
start(controller) {
|
||||
wasm.intounderlyingbytesource_start(this.ptr, addHeapObject(controller));
|
||||
}
|
||||
/**
|
||||
* @param {any} controller
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
pull(controller) {
|
||||
const ret = wasm.intounderlyingbytesource_pull(this.ptr, addHeapObject(controller));
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
cancel() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.intounderlyingbytesource_cancel(ptr);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingSink {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_intounderlyingsink_free(ptr);
|
||||
}
|
||||
/**
|
||||
* @param {any} chunk
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
write(chunk) {
|
||||
const ret = wasm.intounderlyingsink_write(this.ptr, addHeapObject(chunk));
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
close() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
const ret = wasm.intounderlyingsink_close(ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
* @param {any} reason
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
abort(reason) {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
const ret = wasm.intounderlyingsink_abort(ptr, addHeapObject(reason));
|
||||
return takeObject(ret);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class IntoUnderlyingSource {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_intounderlyingsource_free(ptr);
|
||||
}
|
||||
/**
|
||||
* @param {any} controller
|
||||
* @returns {Promise<any>}
|
||||
*/
|
||||
pull(controller) {
|
||||
const ret = wasm.intounderlyingsource_pull(this.ptr, addHeapObject(controller));
|
||||
return takeObject(ret);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
cancel() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.intounderlyingsource_cancel(ptr);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Raw options for [`pipeTo()`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/pipeTo).
|
||||
*/
|
||||
export class PipeOptions {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_pipeoptions_free(ptr);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get preventClose() {
|
||||
const ret = wasm.pipeoptions_preventClose(this.ptr);
|
||||
return ret !== 0;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get preventCancel() {
|
||||
const ret = wasm.pipeoptions_preventCancel(this.ptr);
|
||||
return ret !== 0;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get preventAbort() {
|
||||
const ret = wasm.pipeoptions_preventAbort(this.ptr);
|
||||
return ret !== 0;
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get signal() {
|
||||
const ret = wasm.pipeoptions_signal(this.ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class QueuingStrategy {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_queuingstrategy_free(ptr);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get highWaterMark() {
|
||||
const ret = wasm.queuingstrategy_highWaterMark(this.ptr);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Raw options for [`getReader()`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/getReader).
|
||||
*/
|
||||
export class ReadableStreamGetReaderOptions {
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_readablestreamgetreaderoptions_free(ptr);
|
||||
}
|
||||
/**
|
||||
*/
|
||||
get mode() {
|
||||
const ret = wasm.readablestreamgetreaderoptions_mode(this.ptr);
|
||||
return takeObject(ret);
|
||||
}
|
||||
}
|
||||
/**
|
||||
*/
|
||||
export class ServerConfig {
|
||||
|
||||
static __wrap(ptr) {
|
||||
const obj = Object.create(ServerConfig.prototype);
|
||||
obj.ptr = ptr;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
__destroy_into_raw() {
|
||||
const ptr = this.ptr;
|
||||
this.ptr = 0;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
free() {
|
||||
const ptr = this.__destroy_into_raw();
|
||||
wasm.__wbg_serverconfig_free(ptr);
|
||||
}
|
||||
/**
|
||||
* @param {AsyncIterator<any>} into_server
|
||||
* @param {WritableStream} from_server
|
||||
*/
|
||||
constructor(into_server, from_server) {
|
||||
const ret = wasm.serverconfig_new(addHeapObject(into_server), addHeapObject(from_server));
|
||||
return ServerConfig.__wrap(ret);
|
||||
}
|
||||
}
|
||||
|
||||
async function load(module, imports) {
|
||||
if (typeof Response === 'function' && module instanceof Response) {
|
||||
if (typeof WebAssembly.instantiateStreaming === 'function') {
|
||||
try {
|
||||
return await WebAssembly.instantiateStreaming(module, imports);
|
||||
|
||||
} catch (e) {
|
||||
if (module.headers.get('Content-Type') != 'application/wasm') {
|
||||
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
|
||||
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bytes = await module.arrayBuffer();
|
||||
return await WebAssembly.instantiate(bytes, imports);
|
||||
|
||||
} else {
|
||||
const instance = await WebAssembly.instantiate(module, imports);
|
||||
|
||||
if (instance instanceof WebAssembly.Instance) {
|
||||
return { instance, module };
|
||||
|
||||
} else {
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getImports() {
|
||||
const imports = {};
|
||||
imports.wbg = {};
|
||||
imports.wbg.__wbindgen_object_drop_ref = function(arg0) {
|
||||
takeObject(arg0);
|
||||
};
|
||||
imports.wbg.__wbindgen_cb_drop = function(arg0) {
|
||||
const obj = takeObject(arg0).original;
|
||||
if (obj.cnt-- == 1) {
|
||||
obj.a = 0;
|
||||
return true;
|
||||
}
|
||||
const ret = false;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_string_new = function(arg0, arg1) {
|
||||
const ret = getStringFromWasm0(arg0, arg1);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_respond_553c92471e2e621b = function(arg0, arg1) {
|
||||
getObject(arg0).respond(arg1 >>> 0);
|
||||
};
|
||||
imports.wbg.__wbindgen_object_clone_ref = function(arg0) {
|
||||
const ret = getObject(arg0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_string_get = function(arg0, arg1) {
|
||||
const obj = getObject(arg1);
|
||||
const ret = typeof(obj) === 'string' ? obj : undefined;
|
||||
var ptr0 = isLikeNone(ret) ? 0 : passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
var len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_write_c54a0ef6d718320b = function(arg0, arg1) {
|
||||
const ret = getObject(arg0).write(takeObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_ready_16241896c422ec02 = function(arg0) {
|
||||
const ret = getObject(arg0).ready;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_releaseLock_5b88080d80d6736c = function(arg0) {
|
||||
getObject(arg0).releaseLock();
|
||||
};
|
||||
imports.wbg.__wbg_close_661a81815170ae58 = function(arg0) {
|
||||
const ret = getObject(arg0).close();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getWriter_8c2eade28733ae9d = function() { return handleError(function (arg0) {
|
||||
const ret = getObject(arg0).getWriter();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_close_146359de33accd5b = function(arg0) {
|
||||
getObject(arg0).close();
|
||||
};
|
||||
imports.wbg.__wbg_enqueue_eb446030ed643eb5 = function(arg0, arg1) {
|
||||
getObject(arg0).enqueue(getObject(arg1));
|
||||
};
|
||||
imports.wbg.__wbg_byobRequest_3e1cb18a6efca4e4 = function(arg0) {
|
||||
const ret = getObject(arg0).byobRequest;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_view_7ad18ad26d71d774 = function(arg0) {
|
||||
const ret = getObject(arg0).view;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_byteLength_6f22329fd199ce7d = function(arg0) {
|
||||
const ret = getObject(arg0).byteLength;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_close_26866406ee8e0abb = function(arg0) {
|
||||
getObject(arg0).close();
|
||||
};
|
||||
imports.wbg.__wbg_buffer_f00028fd9efc903b = function(arg0) {
|
||||
const ret = getObject(arg0).buffer;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_byteOffset_b3edd58064ebb082 = function(arg0) {
|
||||
const ret = getObject(arg0).byteOffset;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_bytesliteral_1860f600f905fea0 = function() {
|
||||
const ret = bytes_literal();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_693216e109162396 = function() {
|
||||
const ret = new Error();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_stack_0ddaca5d1abfb52f = function(arg0, arg1) {
|
||||
const ret = getObject(arg1).stack;
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbg_error_09919627ac0992f5 = function(arg0, arg1) {
|
||||
try {
|
||||
console.error(getStringFromWasm0(arg0, arg1));
|
||||
} finally {
|
||||
wasm.__wbindgen_free(arg0, arg1);
|
||||
}
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Window_a2a08d3918d7d4d0 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof Window;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_document_14a383364c173445 = function(arg0) {
|
||||
const ret = getObject(arg0).document;
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_getElementById_0c9415d96f5b9ec6 = function(arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).getElementById(getStringFromWasm0(arg1, arg2));
|
||||
return isLikeNone(ret) ? 0 : addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_style_3fb37aa4b3701322 = function(arg0) {
|
||||
const ret = getObject(arg0).style;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_setvalue_61440ce246b0279d = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).value = getStringFromWasm0(arg1, arg2);
|
||||
};
|
||||
imports.wbg.__wbg_log_7761a8b8a8c1864e = function(arg0) {
|
||||
console.log(getObject(arg0));
|
||||
};
|
||||
imports.wbg.__wbg_setProperty_88447bf87ac638d7 = function() { return handleError(function (arg0, arg1, arg2, arg3, arg4) {
|
||||
getObject(arg0).setProperty(getStringFromWasm0(arg1, arg2), getStringFromWasm0(arg3, arg4));
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_newnoargs_fc5356289219b93b = function(arg0, arg1) {
|
||||
const ret = new Function(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_is_object = function(arg0) {
|
||||
const val = getObject(arg0);
|
||||
const ret = typeof(val) === 'object' && val !== null;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_done_2a1e30464aae6a4d = function(arg0) {
|
||||
const ret = getObject(arg0).done;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_value_a495c29471c31da6 = function(arg0) {
|
||||
const ret = getObject(arg0).value;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_call_4573f605ca4b5f10 = function() { return handleError(function (arg0, arg1) {
|
||||
const ret = getObject(arg0).call(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_self_ba1ddafe9ea7a3a2 = function() { return handleError(function () {
|
||||
const ret = self.self;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_window_be3cc430364fd32c = function() { return handleError(function () {
|
||||
const ret = window.window;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_globalThis_56d9c9f814daeeee = function() { return handleError(function () {
|
||||
const ret = globalThis.globalThis;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_global_8c35aeee4ac77f2b = function() { return handleError(function () {
|
||||
const ret = global.global;
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbindgen_is_undefined = function(arg0) {
|
||||
const ret = getObject(arg0) === undefined;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_new_651776e932b7e9c7 = function(arg0, arg1) {
|
||||
const ret = new Error(getStringFromWasm0(arg0, arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_call_9855a4612eb496cb = function() { return handleError(function (arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).call(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_next_e7700fbea331c507 = function() { return handleError(function (arg0) {
|
||||
const ret = getObject(arg0).next();
|
||||
return addHeapObject(ret);
|
||||
}, arguments) };
|
||||
imports.wbg.__wbg_toString_81e19471abb6dc98 = function(arg0) {
|
||||
const ret = getObject(arg0).toString();
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_78403b138428b684 = function(arg0, arg1) {
|
||||
try {
|
||||
var state0 = {a: arg0, b: arg1};
|
||||
var cb0 = (arg0, arg1) => {
|
||||
const a = state0.a;
|
||||
state0.a = 0;
|
||||
try {
|
||||
return __wbg_adapter_107(a, state0.b, arg0, arg1);
|
||||
} finally {
|
||||
state0.a = a;
|
||||
}
|
||||
};
|
||||
const ret = new Promise(cb0);
|
||||
return addHeapObject(ret);
|
||||
} finally {
|
||||
state0.a = state0.b = 0;
|
||||
}
|
||||
};
|
||||
imports.wbg.__wbg_resolve_f269ce174f88b294 = function(arg0) {
|
||||
const ret = Promise.resolve(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_1c698eedca15eed6 = function(arg0, arg1) {
|
||||
const ret = getObject(arg0).then(getObject(arg1));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_then_4debc41d4fc92ce5 = function(arg0, arg1, arg2) {
|
||||
const ret = getObject(arg0).then(getObject(arg1), getObject(arg2));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_buffer_de1150f91b23aa89 = function(arg0) {
|
||||
const ret = getObject(arg0).buffer;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_newwithbyteoffsetandlength_9ca61320599a2c84 = function(arg0, arg1, arg2) {
|
||||
const ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_new_97cf52648830a70d = function(arg0) {
|
||||
const ret = new Uint8Array(getObject(arg0));
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbg_set_a0172b213e2469e9 = function(arg0, arg1, arg2) {
|
||||
getObject(arg0).set(getObject(arg1), arg2 >>> 0);
|
||||
};
|
||||
imports.wbg.__wbg_length_e09c0b925ab8de5d = function(arg0) {
|
||||
const ret = getObject(arg0).length;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbg_instanceof_Uint8Array_fd17ec67c77de602 = function(arg0) {
|
||||
const ret = getObject(arg0) instanceof Uint8Array;
|
||||
return ret;
|
||||
};
|
||||
imports.wbg.__wbindgen_debug_string = function(arg0, arg1) {
|
||||
const ret = debugString(getObject(arg1));
|
||||
const ptr0 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
|
||||
const len0 = WASM_VECTOR_LEN;
|
||||
getInt32Memory0()[arg0 / 4 + 1] = len0;
|
||||
getInt32Memory0()[arg0 / 4 + 0] = ptr0;
|
||||
};
|
||||
imports.wbg.__wbindgen_throw = function(arg0, arg1) {
|
||||
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||
};
|
||||
imports.wbg.__wbindgen_memory = function() {
|
||||
const ret = wasm.memory;
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
imports.wbg.__wbindgen_closure_wrapper3620 = function(arg0, arg1, arg2) {
|
||||
const ret = makeMutClosure(arg0, arg1, 1230, __wbg_adapter_22);
|
||||
return addHeapObject(ret);
|
||||
};
|
||||
|
||||
return imports;
|
||||
}
|
||||
|
||||
function initMemory(imports, maybe_memory) {
|
||||
|
||||
}
|
||||
|
||||
function finalizeInit(instance, module) {
|
||||
wasm = instance.exports;
|
||||
init.__wbindgen_wasm_module = module;
|
||||
cachedInt32Memory0 = new Int32Array(wasm.memory.buffer);
|
||||
cachedUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||
|
||||
|
||||
return wasm;
|
||||
}
|
||||
|
||||
function initSync(bytes) {
|
||||
const imports = getImports();
|
||||
|
||||
initMemory(imports);
|
||||
|
||||
const module = new WebAssembly.Module(bytes);
|
||||
const instance = new WebAssembly.Instance(module, imports);
|
||||
|
||||
return finalizeInit(instance, module);
|
||||
}
|
||||
|
||||
async function init(input) {
|
||||
if (typeof input === 'undefined') {
|
||||
input = new URL('src_lsp_browser_bg.wasm', import.meta.url);
|
||||
}
|
||||
const imports = getImports();
|
||||
|
||||
if (typeof input === 'string' || (typeof Request === 'function' && input instanceof Request) || (typeof URL === 'function' && input instanceof URL)) {
|
||||
input = fetch(input);
|
||||
}
|
||||
|
||||
initMemory(imports);
|
||||
|
||||
const { instance, module } = await load(await input, imports);
|
||||
|
||||
return finalizeInit(instance, module);
|
||||
}
|
||||
|
||||
export { initSync }
|
||||
export default init;
|
BIN
packages/app/assets/wasm/src_lsp_browser_bg.wasm
Normal file
BIN
packages/app/assets/wasm/src_lsp_browser_bg.wasm
Normal file
Binary file not shown.
35
packages/app/assets/wasm/src_lsp_browser_bg.wasm.d.ts
vendored
Normal file
35
packages/app/assets/wasm/src_lsp_browser_bg.wasm.d.ts
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
/* tslint:disable */
|
||||
/* eslint-disable */
|
||||
export const memory: WebAssembly.Memory;
|
||||
export function __wbg_serverconfig_free(a: number): void;
|
||||
export function serve(a: number): number;
|
||||
export function serverconfig_new(a: number, b: number): number;
|
||||
export function __wbg_queuingstrategy_free(a: number): void;
|
||||
export function queuingstrategy_highWaterMark(a: number): number;
|
||||
export function __wbg_readablestreamgetreaderoptions_free(a: number): void;
|
||||
export function readablestreamgetreaderoptions_mode(a: number): number;
|
||||
export function __wbg_pipeoptions_free(a: number): void;
|
||||
export function pipeoptions_preventClose(a: number): number;
|
||||
export function pipeoptions_preventCancel(a: number): number;
|
||||
export function pipeoptions_preventAbort(a: number): number;
|
||||
export function pipeoptions_signal(a: number): number;
|
||||
export function __wbg_intounderlyingbytesource_free(a: number): void;
|
||||
export function intounderlyingbytesource_type(a: number): number;
|
||||
export function intounderlyingbytesource_autoAllocateChunkSize(a: number): number;
|
||||
export function intounderlyingbytesource_start(a: number, b: number): void;
|
||||
export function intounderlyingbytesource_pull(a: number, b: number): number;
|
||||
export function intounderlyingbytesource_cancel(a: number): void;
|
||||
export function __wbg_intounderlyingsource_free(a: number): void;
|
||||
export function intounderlyingsource_pull(a: number, b: number): number;
|
||||
export function intounderlyingsource_cancel(a: number): void;
|
||||
export function __wbg_intounderlyingsink_free(a: number): void;
|
||||
export function intounderlyingsink_write(a: number, b: number): number;
|
||||
export function intounderlyingsink_close(a: number): number;
|
||||
export function intounderlyingsink_abort(a: number, b: number): number;
|
||||
export function __wbindgen_malloc(a: number): number;
|
||||
export function __wbindgen_realloc(a: number, b: number, c: number): number;
|
||||
export const __wbindgen_export_2: WebAssembly.Table;
|
||||
export function _dyn_core__ops__function__FnMut__A____Output___R_as_wasm_bindgen__closure__WasmClosure___describe__invoke__ha2071623bfe400a9(a: number, b: number, c: number): void;
|
||||
export function __wbindgen_exn_store(a: number): void;
|
||||
export function __wbindgen_free(a: number, b: number): void;
|
||||
export function wasm_bindgen__convert__closures__invoke2_mut__h18cbcd62478d99de(a: number, b: number, c: number, d: number): void;
|
8
packages/app/build.sh
Executable file
8
packages/app/build.sh
Executable file
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
# FILEPATH: /workspaces/libsrc/packages/app/build.sh
|
||||
cargo build --release -p src-lsp-browser --target wasm32-unknown-unknown;
|
||||
wasm-bindgen --out-dir assets/wasm --target web --typescript /scratch/cargo_target/wasm32-unknown-unknown/release/src_lsp_browser.wasm;
|
||||
webpack;
|
||||
mv ../../book/playground/*.wasm ../../book
|
||||
mv ../../book/playground/*.ttf ../../book
|
||||
cp ../../book/taocp.png ../../book/playground
|
1
packages/app/declarations.d.ts
vendored
Normal file
1
packages/app/declarations.d.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
declare module "*.module.css";
|
BIN
packages/app/dist/5bf53257fccc841019f6.wasm
vendored
Normal file
BIN
packages/app/dist/5bf53257fccc841019f6.wasm
vendored
Normal file
Binary file not shown.
BIN
packages/app/dist/ade705761eb7e702770d.ttf
vendored
Normal file
BIN
packages/app/dist/ade705761eb7e702770d.ttf
vendored
Normal file
Binary file not shown.
2
packages/app/dist/app.bundle.js
vendored
Normal file
2
packages/app/dist/app.bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
packages/app/dist/app.bundle.js.LICENSE.txt
vendored
Normal file
1
packages/app/dist/app.bundle.js.LICENSE.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/*! @license DOMPurify 2.3.1 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.1/LICENSE */
|
1
packages/app/dist/editor.worker.bundle.js
vendored
Normal file
1
packages/app/dist/editor.worker.bundle.js
vendored
Normal file
File diff suppressed because one or more lines are too long
65
packages/app/dist/index.html
vendored
Normal file
65
packages/app/dist/index.html
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
<!doctype html><html lang="en" class="dark" dir=""><head><meta charset="UTF-8"><title>Playground</title><meta name="description" content=""><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#ffffff"><link rel="shortcut icon" href="taocp.png"><link rel="stylesheet" href="css/variables.css"><link rel="stylesheet" href="css/general.css"><link rel="stylesheet" href="css/chrome.css"><link rel="stylesheet" href="css/print.css" media="print"><link rel="stylesheet" href="FontAwesome/css/font-awesome.css"><link rel="stylesheet" href="fonts/fonts.css"><link rel="stylesheet" href="highlight.css"><link rel="stylesheet" href="tomorrow-night.css"><link rel="stylesheet" href="ayu-highlight.css"><link rel="stylesheet" href="ok.css"><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Allura&family=Inclusive+Sans:ital@0;1&display=swap" rel="stylesheet"><script type="module" src="app.bundle.js"></script><script type="module" src="editor.worker.bundle.js"></script></head><body class="sidebar-visible no-js"><div id="body-container"><script>var path_to_root = "";
|
||||
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "rust" : "dark";</script><script>try {
|
||||
var theme = localStorage.getItem('mdbook-theme');
|
||||
var sidebar = localStorage.getItem('mdbook-sidebar');
|
||||
|
||||
if (theme.startsWith('"') && theme.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
||||
}
|
||||
|
||||
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
||||
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
||||
}
|
||||
} catch (e) { }</script><script>var theme;
|
||||
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
||||
if (theme === null || theme === undefined) { theme = default_theme; }
|
||||
var html = document.querySelector('html');
|
||||
html.classList.remove('dark')
|
||||
html.classList.add(theme);
|
||||
var body = document.querySelector('body');
|
||||
body.classList.remove('no-js')
|
||||
body.classList.add('js');</script><input type="checkbox" id="sidebar-toggle-anchor" class="hidden"><script>var body = document.querySelector('body');
|
||||
var sidebar = null;
|
||||
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
|
||||
if (document.body.clientWidth >= 1080) {
|
||||
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
||||
sidebar = sidebar || 'visible';
|
||||
} else {
|
||||
sidebar = 'hidden';
|
||||
}
|
||||
sidebar_toggle.checked = sidebar === 'visible';
|
||||
body.classList.remove('sidebar-visible');
|
||||
body.classList.add("sidebar-" + sidebar);</script><nav id="sidebar" class="sidebar" aria-label="Table of contents"><div class="sidebar-scrollbox"><ol class="chapter"><li class="chapter-item expanded"><a href="0intro.html"><strong aria-hidden="true">1.</strong> Intro</a></li><li class="chapter-item expanded affix"></li><li class="part-title">Language</li><li class="chapter-item expanded"><a href="language/0intro.html"><strong aria-hidden="true">2.</strong> Language</a></li><li><ol class="section"><li class="chapter-item expanded"><a href="examples.html"><strong aria-hidden="true">2.1.</strong> Examples</a></li></ol></li><li class="chapter-item expanded"></li><li class="part-title">Playground</li><li class="chapter-item expanded"><a href="playground.html" class="active"><strong aria-hidden="true">3.</strong> Playground</a></li></ol></div><div id="sidebar-resize-handle" class="sidebar-resize-handle"><div class="sidebar-resize-indicator"></div></div></nav><script>var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
|
||||
sidebarScrollbox.addEventListener('click', function(e) {
|
||||
if (e.target.tagName === 'A') {
|
||||
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
|
||||
}
|
||||
}, { passive: true });
|
||||
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
|
||||
sessionStorage.removeItem('sidebar-scroll');
|
||||
if (sidebarScrollTop) {
|
||||
// preserve sidebar scroll position when navigating via links within sidebar
|
||||
sidebarScrollbox.scrollTop = sidebarScrollTop;
|
||||
} else {
|
||||
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
|
||||
var activeSection = document.querySelector('#sidebar .active');
|
||||
if (activeSection) {
|
||||
activeSection.scrollIntoView({ block: 'center' });
|
||||
}
|
||||
}</script><div id="page-wrapper" class="page-wrapper"><div class="page"><div id="menu-bar-hover-placeholder"></div><div id="menu-bar" class="menu-bar sticky"><div class="left-buttons"><label id="sidebar-toggle" class="nes-btn" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">🍔</label> <button id="theme-toggle" class="nes-btn" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">💅</button><ul id="theme-list" class="is-primary theme-popup" aria-label="Themes" role="menu"><li role="none"><button role="menuitem" class="theme" id="light">Light</button></li><li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li><li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li><li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li><li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li></ul><button id="search-toggle" class="nes-btn is-primary" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">🔎</button></div><h1 class="menu-title"></h1><div class="right-buttons"><a href="print.html" title="Print this book" aria-label="Print this book" class="nes-btn">🖨️ </a><a class="nes-btn" href="https://ok.software/ok/src" title="branch" aria-label="branch">🌿</a></div></div><div id="search-wrapper" class="hidden"><form id="searchbar-outer" class="searchbar-outer"><input type="search" id="searchbar" class="nes-field" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header"></form><div id="searchresults-outer" class="searchresults-outer hidden"><div id="searchresults-header" class="searchresults-header"></div><ul id="searchresults"></ul></div></div><script>document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
||||
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
||||
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
||||
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
||||
});</script><div id="content" class="content"><main><div id="container"><div id="cell-editor"><label for="editor">editor</label><div id="editor"></div></div><div id="cell-syntax"><label for="channel-syntax">syntax</label> <textarea id="channel-syntax" autocomplete="off" spellcheck="off" wrap="off" readonly="readonly"></textarea></div><div id="cell-console"><label for="channel-console">console</label> <textarea id="channel-console" autocomplete="off" spellcheck="off" wrap="off" readonly="readonly" rows="3"></textarea></div><div id="cell-client"><label for="channel-client">message trace (client ⇒ server)</label> <textarea id="channel-client" autocomplete="off" spellcheck="off" wrap="off" readonly="readonly" rows="4"></textarea></div><div id="cell-server"><label for="channel-server">message trace (client ⇐ server)</label> <textarea id="channel-server" autocomplete="off" spellcheck="off" wrap="off" readonly="readonly" rows="4"></textarea></div></div></main><nav class="nav-wrapper" aria-label="Page navigation"><a rel="prev" href="examples.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAzElEQVRYR+2X0Q6AIAhF5f8/2jYXZkwEjNSVvVUjDpcrGgT7FUkI2D9xRfQETwNIiWO85wfINfQUEyxBG2ArsLwC0jioGt5zFcwF4OYDPi/mBYKm4t0U8ATgRm3ThFoAqkhNgWkA0jJLvaOVSs7j3qMnSgXWBMiWPXe94QqMBMBc1VZIvaTu5u5pQewq0EqNZvIEMCmxAawK0DNkay9QmfFNAJUXfgGgUkLaE7j/h8fnASkxHTz0DGIBMCnBeeM7AArpUd3mz2x3C7wADglA8BcWMZhZAAAAAElFTkSuQmCC" style="rotate: 270deg; width: 94px"/></a><div style="clear: both"></div></nav></div></div><nav class="nav-wide-wrapper" aria-label="Page navigation"><a rel="next prefetch" href="examples.html" class="nav-chapters previous" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right" style="display: flex; justify-content: middle; align-items: center; width: 50%"><</a></nav></div><script>const wsProtocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsAddress = wsProtocol + "//" + location.host + "/" + "__livereload";
|
||||
const socket = new WebSocket(wsAddress);
|
||||
socket.onmessage = function (event) {
|
||||
if (event.data === "reload") {
|
||||
socket.close();
|
||||
location.reload();
|
||||
}
|
||||
};
|
||||
|
||||
window.onbeforeunload = function() {
|
||||
socket.close();
|
||||
}</script><script>window.playground_copyable = true;</script><script src="elasticlunr.min.js"></script><script src="mark.min.js"></script><script src="searcher.js"></script><script src="clipboard.min.js"></script><script src="highlight.js"></script><script src="book.js"></script></div></body></html>
|
58
packages/app/package.json
Normal file
58
packages/app/package.json
Normal file
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"private": true,
|
||||
"name": "monaco-lsp-streams",
|
||||
"description": "",
|
||||
"version": "0.0.0",
|
||||
"license": "Apache-2.0 WITH LLVM-exception",
|
||||
"author": {
|
||||
"name": "silvanshade",
|
||||
"email": "silvanshade@users.noreply.github.com",
|
||||
"url": "https://github.com/silvanshade"
|
||||
},
|
||||
"homepage": "https://github.com/silvanshade/monaco-lsp-streams#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/silvanshade/monaco-lsp-streams.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/silvanshade/monaco-lsp-streams/issues"
|
||||
},
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "./build.sh",
|
||||
"format": "prettier --write '**/*.{js,json,ts,tsx,yml,yaml}'",
|
||||
"lint": "eslint 'src/**/*.{js,ts,tsx}' && prettier --check '**/*.{json,yml,yaml}'",
|
||||
"app": "webpack serve --open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/debounce": "^1.2.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.27.0",
|
||||
"@typescript-eslint/parser": "^5.27.0",
|
||||
"clean-webpack-plugin": "^4.0.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"esbuild-loader": "^2.19.0",
|
||||
"eslint": "^8.17.0",
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"html-webpack-plugin": "^5.5.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"prettier": "^2.6.2",
|
||||
"source-map-loader": "^4.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"terser-webpack-plugin": "^5.3.3",
|
||||
"ts-node": "^10.8.1",
|
||||
"typescript": "^4.7.3",
|
||||
"webpack": "^5.73.0",
|
||||
"webpack-cli": "^4.9.2",
|
||||
"webpack-dev-server": "^4.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"debounce": "^1.2.1",
|
||||
"json-rpc-2.0": "^1.3.0",
|
||||
"monaco-editor-core": "^0.33.0",
|
||||
"monaco-languageclient": "^1.0.1",
|
||||
"vscode-languageserver-protocol": "^3.17.1"
|
||||
}
|
||||
}
|
118
packages/app/src/app.ts
Normal file
118
packages/app/src/app.ts
Normal file
|
@ -0,0 +1,118 @@
|
|||
import debounce from "debounce";
|
||||
import * as monaco from "monaco-editor-core";
|
||||
import { MonacoToProtocolConverter } from "monaco-languageclient";
|
||||
import * as proto from "vscode-languageserver-protocol";
|
||||
|
||||
import Client from "./client";
|
||||
import { FromServer, IntoServer } from "./codec";
|
||||
import Language from "./language";
|
||||
import Server from "./server";
|
||||
|
||||
class Environment implements monaco.Environment {
|
||||
getWorkerUrl(moduleId: string, label: string) {
|
||||
if (label === "editorWorkerService") {
|
||||
return "./editor.worker.bundle.js";
|
||||
}
|
||||
throw new Error(`getWorkerUrl: unexpected ${JSON.stringify({ moduleId, label })}`);
|
||||
}
|
||||
}
|
||||
|
||||
const monacoToProtocol = new MonacoToProtocolConverter(monaco);
|
||||
|
||||
export default class App {
|
||||
readonly #window: Window & monaco.Window & typeof globalThis = self;
|
||||
|
||||
readonly #intoServer: IntoServer = new IntoServer();
|
||||
readonly #fromServer: FromServer = FromServer.create();
|
||||
|
||||
initializeMonaco(): void {
|
||||
this.#window.MonacoEnvironment = new Environment();
|
||||
}
|
||||
|
||||
createModel(client: Client): monaco.editor.ITextModel {
|
||||
const language = Language.initialize(client);
|
||||
|
||||
const value = `use { host } from std
|
||||
|
||||
effect Make: async + throws + execs + reads + writes {
|
||||
catch() [throws]
|
||||
await<T>(f: Future<T>) [async, throws] -> T
|
||||
exec(arg0: string, args: stringvec) [Make] -> i32
|
||||
}
|
||||
|
||||
struct Local {
|
||||
host: host
|
||||
}
|
||||
|
||||
impl Make for Local {
|
||||
fn catch(self) [throws] {
|
||||
}
|
||||
fn await<T>(f: Future<T>) [async, trhows] -> T {
|
||||
yield()
|
||||
}
|
||||
fn exec(self, arg0: string, args: vec<string>) [Vm] -> i32 {
|
||||
self.host.read("jobserver")
|
||||
if self.host.exec(arg0, args) {
|
||||
raise(1)
|
||||
}
|
||||
}
|
||||
}`.replace(/^\s*\n/gm, "");
|
||||
const id = language.id;
|
||||
const uri = monaco.Uri.parse("inmemory://exec.src");
|
||||
|
||||
const model = monaco.editor.createModel(value, id, uri);
|
||||
|
||||
model.onDidChangeContent(
|
||||
debounce(() => {
|
||||
const text = model.getValue();
|
||||
client.notify(proto.DidChangeTextDocumentNotification.type.method, {
|
||||
textDocument: {
|
||||
version: 0,
|
||||
uri: model.uri.toString(),
|
||||
},
|
||||
contentChanges: [
|
||||
{
|
||||
range: monacoToProtocol.asRange(model.getFullModelRange()),
|
||||
text,
|
||||
},
|
||||
],
|
||||
} as proto.DidChangeTextDocumentParams);
|
||||
}, 200),
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
client.pushAfterInitializeHook(async () => {
|
||||
client.notify(proto.DidOpenTextDocumentNotification.type.method, {
|
||||
textDocument: {
|
||||
uri: model.uri.toString(),
|
||||
languageId: language.id,
|
||||
version: 0,
|
||||
text: model.getValue(),
|
||||
},
|
||||
} as proto.DidOpenTextDocumentParams);
|
||||
});
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
createEditor(client: Client): void {
|
||||
// get .content main element and set the max-width to 100%
|
||||
document.querySelector(".content")?.querySelector("main")?.setAttribute("style", "max-width: 100%;");
|
||||
// get nav-chapters previous element and set the display to none
|
||||
document.querySelector(".nav-chapters")?.setAttribute("style", "display: none;");
|
||||
const container = document.getElementById("editor")!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
this.initializeMonaco();
|
||||
const model = this.createModel(client);
|
||||
monaco.editor.create(container, {
|
||||
model,
|
||||
automaticLayout: true,
|
||||
});
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const client = new Client(this.#fromServer, this.#intoServer);
|
||||
const server = await Server.initialize(this.#intoServer, this.#fromServer);
|
||||
this.createEditor(client);
|
||||
await Promise.all([server.start(), client.start()]);
|
||||
}
|
||||
}
|
87
packages/app/src/client.ts
Normal file
87
packages/app/src/client.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import * as jsrpc from "json-rpc-2.0";
|
||||
import * as proto from "vscode-languageserver-protocol";
|
||||
|
||||
import { Codec, FromServer, IntoServer } from "./codec";
|
||||
|
||||
const consoleChannel = document.getElementById("channel-console") as HTMLTextAreaElement;
|
||||
|
||||
export default class Client extends jsrpc.JSONRPCServerAndClient {
|
||||
afterInitializedHooks: (() => Promise<void>)[] = [];
|
||||
#fromServer: FromServer;
|
||||
|
||||
constructor(fromServer: FromServer, intoServer: IntoServer) {
|
||||
super(
|
||||
new jsrpc.JSONRPCServer(),
|
||||
new jsrpc.JSONRPCClient(async (json: jsrpc.JSONRPCRequest) => {
|
||||
const encoded = Codec.encode(json);
|
||||
intoServer.enqueue(encoded);
|
||||
if (null != json.id) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const response = await fromServer.responses.get(json.id)!;
|
||||
this.client.receive(response as jsrpc.JSONRPCResponse);
|
||||
}
|
||||
}),
|
||||
);
|
||||
this.#fromServer = fromServer;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
// process "window/logMessage": client <- server
|
||||
this.addMethod(proto.LogMessageNotification.type.method, (params) => {
|
||||
const { type, message } = params as { type: proto.MessageType; message: string };
|
||||
switch (type) {
|
||||
case proto.MessageType.Error: {
|
||||
consoleChannel.value += "[error] ";
|
||||
break;
|
||||
}
|
||||
case proto.MessageType.Warning: {
|
||||
consoleChannel.value += " [warn] ";
|
||||
break;
|
||||
}
|
||||
case proto.MessageType.Info: {
|
||||
consoleChannel.value += " [info] ";
|
||||
break;
|
||||
}
|
||||
case proto.MessageType.Log: {
|
||||
consoleChannel.value += " [log] ";
|
||||
break;
|
||||
}
|
||||
}
|
||||
consoleChannel.value += message;
|
||||
consoleChannel.value += "\n";
|
||||
return;
|
||||
});
|
||||
|
||||
// request "initialize": client <-> server
|
||||
await (this.request(proto.InitializeRequest.type.method, {
|
||||
processId: null,
|
||||
clientInfo: {
|
||||
name: "demo-language-client",
|
||||
},
|
||||
capabilities: {},
|
||||
rootUri: null,
|
||||
} as proto.InitializeParams) as Promise<jsrpc.JSONRPCResponse>);
|
||||
|
||||
// notify "initialized": client --> server
|
||||
this.notify(proto.InitializedNotification.type.method, {});
|
||||
|
||||
await Promise.all(this.afterInitializedHooks.map((f: () => Promise<void>) => f()));
|
||||
await Promise.all([this.processNotifications(), this.processRequests()]);
|
||||
}
|
||||
|
||||
async processNotifications(): Promise<void> {
|
||||
for await (const notification of this.#fromServer.notifications) {
|
||||
await this.receiveAndSend(notification);
|
||||
}
|
||||
}
|
||||
|
||||
async processRequests(): Promise<void> {
|
||||
for await (const request of this.#fromServer.requests) {
|
||||
await this.receiveAndSend(request);
|
||||
}
|
||||
}
|
||||
|
||||
pushAfterInitializeHook(...hooks: (() => Promise<void>)[]): void {
|
||||
this.afterInitializedHooks.push(...hooks);
|
||||
}
|
||||
}
|
46
packages/app/src/codec.ts
Normal file
46
packages/app/src/codec.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
import * as jsrpc from "json-rpc-2.0";
|
||||
import * as vsrpc from "vscode-jsonrpc";
|
||||
|
||||
import Bytes from "./codec/bytes";
|
||||
import StreamDemuxer from "./codec/demuxer";
|
||||
import Headers from "./codec/headers";
|
||||
import Queue from "./codec/queue";
|
||||
import Tracer from "./tracer";
|
||||
|
||||
export const encoder = new TextEncoder();
|
||||
export const decoder = new TextDecoder();
|
||||
|
||||
export class Codec {
|
||||
static encode(json: jsrpc.JSONRPCRequest | jsrpc.JSONRPCResponse): Uint8Array {
|
||||
const message = JSON.stringify(json);
|
||||
const delimited = Headers.add(message);
|
||||
return Bytes.encode(delimited);
|
||||
}
|
||||
|
||||
static decode<T>(data: Uint8Array): T {
|
||||
const delimited = Bytes.decode(data);
|
||||
const message = Headers.remove(delimited);
|
||||
return JSON.parse(message) as T;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: tracing effiency
|
||||
export class IntoServer extends Queue<Uint8Array> implements AsyncGenerator<Uint8Array, never, void> {
|
||||
enqueue(item: Uint8Array): void {
|
||||
Tracer.client(Headers.remove(decoder.decode(item)));
|
||||
super.enqueue(item);
|
||||
}
|
||||
}
|
||||
|
||||
export interface FromServer extends WritableStream<Uint8Array> {
|
||||
readonly responses: { get(key: number | string): null | Promise<vsrpc.ResponseMessage> };
|
||||
readonly notifications: AsyncGenerator<vsrpc.NotificationMessage, never, void>;
|
||||
readonly requests: AsyncGenerator<vsrpc.RequestMessage, never, void>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace FromServer {
|
||||
export function create(): FromServer {
|
||||
return new StreamDemuxer();
|
||||
}
|
||||
}
|
28
packages/app/src/codec/bytes.ts
Normal file
28
packages/app/src/codec/bytes.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { encoder, decoder } from "../codec";
|
||||
|
||||
export default class Bytes {
|
||||
static encode(input: string): Uint8Array {
|
||||
return encoder.encode(input);
|
||||
}
|
||||
|
||||
static decode(input: Uint8Array): string {
|
||||
return decoder.decode(input);
|
||||
}
|
||||
|
||||
static append<T extends { length: number; set(arr: T, offset: number): void }>(
|
||||
constructor: { new (length: number): T },
|
||||
...arrays: T[]
|
||||
) {
|
||||
let totalLength = 0;
|
||||
for (const arr of arrays) {
|
||||
totalLength += arr.length;
|
||||
}
|
||||
const result = new constructor(totalLength);
|
||||
let offset = 0;
|
||||
for (const arr of arrays) {
|
||||
result.set(arr, offset);
|
||||
offset += arr.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
73
packages/app/src/codec/demuxer.ts
Normal file
73
packages/app/src/codec/demuxer.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
import * as vsrpc from "vscode-jsonrpc";
|
||||
|
||||
import Bytes from "./bytes";
|
||||
import PromiseMap from "./map";
|
||||
import Queue from "./queue";
|
||||
import Tracer from "../tracer";
|
||||
|
||||
export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
readonly responses: PromiseMap<number | string, vsrpc.ResponseMessage> = new PromiseMap();
|
||||
readonly notifications: Queue<vsrpc.NotificationMessage> = new Queue<vsrpc.NotificationMessage>();
|
||||
readonly requests: Queue<vsrpc.RequestMessage> = new Queue<vsrpc.RequestMessage>();
|
||||
|
||||
readonly #start: Promise<void>;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.#start = this.start();
|
||||
}
|
||||
|
||||
private async start(): Promise<void> {
|
||||
let contentLength: null | number = null;
|
||||
let buffer = new Uint8Array();
|
||||
|
||||
for await (const bytes of this) {
|
||||
buffer = Bytes.append(Uint8Array, buffer, bytes);
|
||||
|
||||
// check if the content length is known
|
||||
if (null == contentLength) {
|
||||
// if not, try to match the prefixed headers
|
||||
const match = Bytes.decode(buffer).match(/^Content-Length:\s*(\d+)\s*/);
|
||||
if (null == match) continue;
|
||||
|
||||
// try to parse the content-length from the headers
|
||||
const length = parseInt(match[1]);
|
||||
if (isNaN(length)) throw new Error("invalid content length");
|
||||
|
||||
// slice the headers since we now have the content length
|
||||
buffer = buffer.slice(match[0].length);
|
||||
|
||||
// set the content length
|
||||
contentLength = length;
|
||||
}
|
||||
|
||||
// if the buffer doesn't contain a full message; await another iteration
|
||||
if (buffer.length < contentLength) continue;
|
||||
|
||||
// decode buffer to a string
|
||||
const delimited = Bytes.decode(buffer);
|
||||
|
||||
// reset the buffer
|
||||
buffer = buffer.slice(contentLength);
|
||||
// reset the contentLength
|
||||
contentLength = null;
|
||||
|
||||
const message = JSON.parse(delimited) as vsrpc.Message;
|
||||
Tracer.server(message);
|
||||
|
||||
// demux the message stream
|
||||
if (vsrpc.Message.isResponse(message) && null != message.id) {
|
||||
this.responses.set(message.id, message);
|
||||
continue;
|
||||
}
|
||||
if (vsrpc.Message.isNotification(message)) {
|
||||
this.notifications.enqueue(message);
|
||||
continue;
|
||||
}
|
||||
if (vsrpc.Message.isRequest(message)) {
|
||||
this.requests.enqueue(message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
packages/app/src/codec/headers.ts
Normal file
9
packages/app/src/codec/headers.ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
export default class Headers {
|
||||
static add(message: string): string {
|
||||
return `Content-Length: ${message.length}\r\n\r\n${message}`;
|
||||
}
|
||||
|
||||
static remove(delimited: string): string {
|
||||
return delimited.replace(/^Content-Length:\s*\d+\s*/, "");
|
||||
}
|
||||
}
|
68
packages/app/src/codec/map.ts
Normal file
68
packages/app/src/codec/map.ts
Normal file
|
@ -0,0 +1,68 @@
|
|||
export default class PromiseMap<K, V extends { toString(): string }> {
|
||||
#map: Map<K, PromiseMap.Entry<V>> = new Map();
|
||||
|
||||
get(key: K & { toString(): string }): null | Promise<V> {
|
||||
let initialized: PromiseMap.Entry<V>;
|
||||
// if the entry doesn't exist, set it
|
||||
if (!this.#map.has(key)) {
|
||||
initialized = this.#set(key);
|
||||
} else {
|
||||
// otherwise return the entry
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
initialized = this.#map.get(key)!;
|
||||
}
|
||||
// if the entry is a pending promise, return it
|
||||
if (initialized.status === "pending") {
|
||||
return initialized.promise;
|
||||
} else {
|
||||
// otherwise return null
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#set(key: K, value?: V): PromiseMap.Entry<V> {
|
||||
if (this.#map.has(key)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.#map.get(key)!;
|
||||
}
|
||||
// placeholder resolver for entry
|
||||
let resolve = (item: V) => {
|
||||
void item;
|
||||
};
|
||||
// promise for entry (which assigns the resolver
|
||||
const promise = new Promise<V>((resolver) => {
|
||||
resolve = resolver;
|
||||
});
|
||||
// the initialized entry
|
||||
const initialized: PromiseMap.Entry<V> = { status: "pending", resolve, promise };
|
||||
if (null != value) {
|
||||
initialized.resolve(value);
|
||||
}
|
||||
// set the entry
|
||||
this.#map.set(key, initialized);
|
||||
return initialized;
|
||||
}
|
||||
|
||||
set(key: K & { toString(): string }, value: V): this {
|
||||
const initialized = this.#set(key, value);
|
||||
// if the promise is pending ...
|
||||
if (initialized.status === "pending") {
|
||||
// ... set the entry status to resolved to free the promise
|
||||
this.#map.set(key, { status: "resolved" });
|
||||
// ... and resolve the promise with the given value
|
||||
initialized.resolve(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.#map.size;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
export namespace PromiseMap {
|
||||
export type Entry<V> =
|
||||
| { status: "pending"; resolve: (item: V) => void; promise: Promise<V> }
|
||||
| { status: "resolved" };
|
||||
}
|
103
packages/app/src/codec/queue.ts
Normal file
103
packages/app/src/codec/queue.ts
Normal file
|
@ -0,0 +1,103 @@
|
|||
export default class Queue<T> implements WritableStream<T>, AsyncGenerator<T, never, void> {
|
||||
readonly #promises: Promise<T>[] = [];
|
||||
readonly #resolvers: ((item: T) => void)[] = [];
|
||||
readonly #observers: ((item: T) => void)[] = [];
|
||||
|
||||
#closed = false;
|
||||
#locked = false;
|
||||
readonly #stream: WritableStream<T>;
|
||||
|
||||
static #__add<X>(promises: Promise<X>[], resolvers: ((item: X) => void)[]): void {
|
||||
promises.push(
|
||||
new Promise((resolve) => {
|
||||
resolvers.push(resolve);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
static #__enqueue<X>(closed: boolean, promises: Promise<X>[], resolvers: ((item: X) => void)[], item: X): void {
|
||||
if (!closed) {
|
||||
if (!resolvers.length) Queue.#__add(promises, resolvers);
|
||||
const resolve = resolvers.shift()!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
resolve(item);
|
||||
}
|
||||
}
|
||||
|
||||
constructor() {
|
||||
const closed = this.#closed;
|
||||
const promises = this.#promises;
|
||||
const resolvers = this.#resolvers;
|
||||
this.#stream = new WritableStream({
|
||||
write(item: T): void {
|
||||
Queue.#__enqueue(closed, promises, resolvers, item);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
#add(): void {
|
||||
return Queue.#__add(this.#promises, this.#resolvers);
|
||||
}
|
||||
|
||||
enqueue(item: T): void {
|
||||
return Queue.#__enqueue(this.#closed, this.#promises, this.#resolvers, item);
|
||||
}
|
||||
|
||||
dequeue(): Promise<T> {
|
||||
if (!this.#promises.length) this.#add();
|
||||
const item = this.#promises.shift()!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
|
||||
return item;
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
return !this.#promises.length;
|
||||
}
|
||||
|
||||
isBlocked(): boolean {
|
||||
return !!this.#resolvers.length;
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.#promises.length - this.#resolvers.length;
|
||||
}
|
||||
|
||||
async next(): Promise<IteratorResult<T, never>> {
|
||||
const done = false;
|
||||
const value = await this.dequeue();
|
||||
for (const observer of this.#observers) {
|
||||
observer(value);
|
||||
}
|
||||
return { done, value };
|
||||
}
|
||||
|
||||
return(): Promise<IteratorResult<T, never>> {
|
||||
return new Promise(() => {
|
||||
// empty
|
||||
});
|
||||
}
|
||||
|
||||
throw(err: Error): Promise<IteratorResult<T, never>> {
|
||||
return new Promise((_resolve, reject) => {
|
||||
reject(err);
|
||||
});
|
||||
}
|
||||
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, never, void> {
|
||||
return this;
|
||||
}
|
||||
|
||||
get locked(): boolean {
|
||||
return this.#stream.locked;
|
||||
}
|
||||
|
||||
abort(reason?: Error): Promise<void> {
|
||||
return this.#stream.abort(reason);
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
return this.#stream.close();
|
||||
}
|
||||
|
||||
getWriter(): WritableStreamDefaultWriter<T> {
|
||||
return this.#stream.getWriter();
|
||||
}
|
||||
}
|
6
packages/app/src/index.ts
Normal file
6
packages/app/src/index.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import "../assets/index.module.css";
|
||||
|
||||
import App from "./app";
|
||||
|
||||
const app = new App();
|
||||
app.run().catch(console.error);
|
69
packages/app/src/language.ts
Normal file
69
packages/app/src/language.ts
Normal file
|
@ -0,0 +1,69 @@
|
|||
// import * as jsrpc from "json-rpc-2.0";
|
||||
import { MonacoToProtocolConverter, ProtocolToMonacoConverter } from "monaco-languageclient";
|
||||
import * as monaco from "monaco-editor-core";
|
||||
import * as proto from "vscode-languageserver-protocol";
|
||||
|
||||
import Client from "./client";
|
||||
|
||||
export const monacoToProtocol = new MonacoToProtocolConverter(monaco);
|
||||
export const protocolToMonaco = new ProtocolToMonacoConverter(monaco);
|
||||
|
||||
let language: null | Language;
|
||||
|
||||
export default class Language implements monaco.languages.ILanguageExtensionPoint {
|
||||
readonly id: string;
|
||||
readonly aliases: string[];
|
||||
readonly extensions: string[];
|
||||
readonly mimetypes: string[];
|
||||
|
||||
private constructor(client: Client) {
|
||||
const { id, aliases, extensions, mimetypes } = Language.extensionPoint();
|
||||
this.id = id;
|
||||
this.aliases = aliases;
|
||||
this.extensions = extensions;
|
||||
this.mimetypes = mimetypes;
|
||||
this.registerLanguage(client);
|
||||
}
|
||||
|
||||
static extensionPoint(): monaco.languages.ILanguageExtensionPoint & {
|
||||
aliases: string[];
|
||||
extensions: string[];
|
||||
mimetypes: string[];
|
||||
} {
|
||||
const id = "src lang";
|
||||
const aliases = ["src"];
|
||||
const extensions = [".src"];
|
||||
const mimetypes = ["text/src"];
|
||||
return { id, extensions, aliases, mimetypes };
|
||||
}
|
||||
|
||||
private registerLanguage(client: Client): void {
|
||||
void client;
|
||||
monaco.languages.register(Language.extensionPoint());
|
||||
monaco.languages.registerDocumentSymbolProvider(this.id, {
|
||||
// eslint-disable-next-line
|
||||
async provideDocumentSymbols(model, token): Promise<monaco.languages.DocumentSymbol[]> {
|
||||
void token;
|
||||
const response = await (client.request(proto.DocumentSymbolRequest.type.method, {
|
||||
textDocument: monacoToProtocol.asTextDocumentIdentifier(model),
|
||||
} as proto.DocumentSymbolParams) as Promise<proto.SymbolInformation[]>);
|
||||
|
||||
const uri = model.uri.toString();
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const result: monaco.languages.DocumentSymbol[] = protocolToMonaco.asSymbolInformations(response, uri);
|
||||
|
||||
return result;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
static initialize(client: Client): Language {
|
||||
if (null == language) {
|
||||
language = new Language(client);
|
||||
} else {
|
||||
console.warn("Language already initialized; ignoring");
|
||||
}
|
||||
return language;
|
||||
}
|
||||
}
|
31
packages/app/src/server.ts
Normal file
31
packages/app/src/server.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import init, { InitOutput, serve, ServerConfig } from "../assets/wasm/src_lsp_browser";
|
||||
import { FromServer, IntoServer } from "./codec";
|
||||
|
||||
let server: null | Server;
|
||||
|
||||
export default class Server {
|
||||
readonly initOutput: InitOutput;
|
||||
readonly #intoServer: IntoServer;
|
||||
readonly #fromServer: FromServer;
|
||||
|
||||
private constructor(initOutput: InitOutput, intoServer: IntoServer, fromServer: FromServer) {
|
||||
this.initOutput = initOutput;
|
||||
this.#intoServer = intoServer;
|
||||
this.#fromServer = fromServer;
|
||||
}
|
||||
|
||||
static async initialize(intoServer: IntoServer, fromServer: FromServer): Promise<Server> {
|
||||
if (null == server) {
|
||||
const initOutput = await init();
|
||||
server = new Server(initOutput, intoServer, fromServer);
|
||||
} else {
|
||||
console.warn("Server already initialized; ignoring");
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
const config = new ServerConfig(this.#intoServer, this.#fromServer);
|
||||
await serve(config);
|
||||
}
|
||||
}
|
17
packages/app/src/tracer.ts
Normal file
17
packages/app/src/tracer.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import * as proto from "vscode-languageserver-protocol";
|
||||
|
||||
const clientChannel = document.getElementById("channel-client") as HTMLTextAreaElement;
|
||||
const serverChannel = document.getElementById("channel-server") as HTMLTextAreaElement;
|
||||
|
||||
export default class Tracer {
|
||||
static client(message: string): void {
|
||||
clientChannel.value += message;
|
||||
clientChannel.value += "\n";
|
||||
}
|
||||
|
||||
static server(input: string | proto.Message): void {
|
||||
const message: string = typeof input === "string" ? input : JSON.stringify(input);
|
||||
serverChannel.value += message;
|
||||
serverChannel.value += "\n";
|
||||
}
|
||||
}
|
8
packages/app/tsconfig.json
Normal file
8
packages/app/tsconfig.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"rootDir": "src"
|
||||
},
|
||||
"exclude": []
|
||||
}
|
94
packages/app/webpack.config.js
Normal file
94
packages/app/webpack.config.js
Normal file
|
@ -0,0 +1,94 @@
|
|||
// @ts-check
|
||||
|
||||
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
const path = require("path");
|
||||
const webpack = require("webpack");
|
||||
|
||||
/** @type {import("webpack").Configuration & { devServer?: import("webpack-dev-server").Configuration } } */
|
||||
const config = {
|
||||
experiments: {
|
||||
asyncWebAssembly: true,
|
||||
},
|
||||
mode: "production",
|
||||
target: "web",
|
||||
entry: {
|
||||
app: "./src/index.ts",
|
||||
"editor.worker": "monaco-editor-core/esm/vs/editor/editor.worker.js",
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
vscode: require.resolve("monaco-languageclient/vscode-compatibility"),
|
||||
},
|
||||
extensions: [".ts", ".js", ".json", ".ttf"],
|
||||
fallback: {
|
||||
fs: false,
|
||||
child_process: false,
|
||||
net: false,
|
||||
crypto: false,
|
||||
path: require.resolve("path-browserify"),
|
||||
},
|
||||
},
|
||||
output: {
|
||||
globalObject: "self",
|
||||
filename: "[name].bundle.js",
|
||||
path: path.resolve(__dirname, "../../book/playground"),
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts?$/,
|
||||
loader: "esbuild-loader",
|
||||
options: {
|
||||
loader: "ts",
|
||||
target: "es2022",
|
||||
minify: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: ["style-loader", "css-loader"],
|
||||
},
|
||||
{
|
||||
test: /\.(woff|woff2|eot|ttf|otf)$/i,
|
||||
type: "asset/resource",
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.ProgressPlugin(),
|
||||
new CleanWebpackPlugin(),
|
||||
// new CopyWebpackPlugin({
|
||||
// patterns: [
|
||||
// {
|
||||
// from: "../../node_modules/web-tree-sitter/tree-sitter.wasm",
|
||||
// },
|
||||
// ],
|
||||
// }),
|
||||
new HtmlWebpackPlugin({
|
||||
template: "../../book/playground/index.html",
|
||||
scriptLoading: "module",
|
||||
title: "src-lsp Playground",
|
||||
}),
|
||||
],
|
||||
optimization: {
|
||||
minimize: true,
|
||||
},
|
||||
performance: {
|
||||
hints: false,
|
||||
},
|
||||
devServer: {
|
||||
static: {
|
||||
directory: path.join(__dirname, "dist"),
|
||||
},
|
||||
compress: true,
|
||||
port: 9000,
|
||||
client: {
|
||||
progress: true,
|
||||
reconnect: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -8,7 +8,7 @@ use salsa::DebugWithDb;
|
|||
|
||||
#[derive(Default)]
|
||||
#[salsa::db(crate::Jar)]
|
||||
pub(crate) struct Database {
|
||||
pub struct Database {
|
||||
storage: salsa::Storage<Self>,
|
||||
|
||||
// The logs are only used for testing and demonstrating reuse:
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use okstd::prelude::debug;
|
||||
|
||||
use crate::{
|
||||
compiler::{errors::Errors},
|
||||
compiler::errors::Errors,
|
||||
parser::ast::{self},
|
||||
Db,
|
||||
};
|
||||
|
||||
use self::text::SourceProgram;
|
||||
|
||||
mod db;
|
||||
mod errors;
|
||||
pub mod db;
|
||||
pub mod errors;
|
||||
pub mod ir;
|
||||
mod tests;
|
||||
pub mod text;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[salsa::tracked]
|
||||
pub fn compile(db: &dyn Db, src: SourceProgram) -> ir::Program {
|
||||
|
@ -23,11 +24,15 @@ pub fn compile(db: &dyn Db, src: SourceProgram) -> ir::Program {
|
|||
let wrapper = crate::lexer::TripleIterator::new(src.text(db));
|
||||
let t = crate::parser::src::SourceParser::new().parse(&mut errors, wrapper);
|
||||
// let mut errors_in_positions: Vec<ir::Position> = vec![];
|
||||
|
||||
if !errors.is_empty() {
|
||||
let spans = text::to_spans(db, src);
|
||||
let _tokens = spans.tokens(db);
|
||||
|
||||
for _error_range in Into::<Errors>::into(errors) {
|
||||
text::to_spans(db, src);
|
||||
|
||||
}
|
||||
panic!();
|
||||
// panic!();
|
||||
}
|
||||
|
||||
let modul = t.unwrap();
|
||||
|
@ -42,7 +47,9 @@ pub fn compile(db: &dyn Db, src: SourceProgram) -> ir::Program {
|
|||
ast::Expression::Binding(_) => todo!(),
|
||||
ast::Expression::FnCall(_) => todo!(),
|
||||
ast::Expression::String(_) => todo!(),
|
||||
ast::Expression::FnDef(_) => {}
|
||||
ast::Expression::FnDef(_) => {
|
||||
debug!("Function definition");
|
||||
}
|
||||
ast::Expression::ShellCommand(_, _) => todo!(),
|
||||
ast::Expression::EffectDef(_) => todo!(),
|
||||
ast::Expression::StructDef(_) => todo!(),
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
#[cfg(test)]
|
||||
|
||||
|
||||
use okstd::prelude::*;
|
||||
|
||||
#[okstd::test]
|
||||
use super::*;
|
||||
|
||||
#[okstd::log(debug)]
|
||||
#[okstd::test]
|
||||
fn debug() {
|
||||
use salsa::{database::AsSalsaDatabase, storage::HasJarsDyn};
|
||||
|
||||
debug!("hello");
|
||||
|
||||
use super::{db, text::SourceProgram};
|
||||
|
||||
let src = r#"use { native_fs, native_exec } from host
|
||||
use { fs } from std
|
||||
|
||||
|
@ -21,7 +14,7 @@ struct Innitguv {
|
|||
current_pid: i32
|
||||
}
|
||||
"#;
|
||||
let db = &crate::compiler::db::Database::default().enable_logging();
|
||||
let db = &db::Database::default().enable_logging();
|
||||
|
||||
let prog = SourceProgram::new(db, src.to_string());
|
||||
let res = super::compile(db, prog);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::ops::Range;
|
||||
|
||||
use bitflags::bitflags;
|
||||
use crate::Db;
|
||||
use bitflags::bitflags;
|
||||
use okstd::prelude::*;
|
||||
|
||||
/// Represents the source program text.
|
||||
#[salsa::input]
|
||||
|
@ -30,9 +31,8 @@ pub struct Spanned {
|
|||
#[salsa::interned]
|
||||
pub struct Span {
|
||||
/// The range of the span in the source program text.
|
||||
pub span: (usize, usize),
|
||||
pub span: Range<usize>
|
||||
}
|
||||
|
||||
/// Represents a position in the source code.
|
||||
#[salsa::interned]
|
||||
pub struct Position {
|
||||
|
@ -80,7 +80,6 @@ fn cmp_range<T: Ord>(a: &Range<T>, b: &Range<T>) -> SpanOverlap {
|
|||
overlap
|
||||
}
|
||||
|
||||
|
||||
/// todo(sevki): split this into two functions
|
||||
#[salsa::tracked]
|
||||
pub fn to_spans(db: &dyn Db, src: SourceProgram) -> SourceMap {
|
||||
|
@ -109,20 +108,25 @@ pub fn to_spans(db: &dyn Db, src: SourceProgram) -> SourceMap {
|
|||
let _size = token.end - token.start;
|
||||
// then we peek at the first line
|
||||
let mut start: Option<(usize, usize)> = None;
|
||||
loop {
|
||||
if let Some((line_no, span)) = line_lengths.clone().peek() {
|
||||
|
||||
while let Some((line_no, span)) = line_lengths.clone().peek() {
|
||||
// if the token is within the line
|
||||
let overlap = cmp_range(&span, &(token.start..token.end));
|
||||
if overlap == SpanOverlap::NONE && start.is_none() {
|
||||
// if the token is not within the line
|
||||
line_lengths.next();
|
||||
}
|
||||
if overlap == SpanOverlap::START || overlap == SpanOverlap::BOTH {
|
||||
// if the token is within the line
|
||||
let overlap = cmp_range(&span, &(token.start..token.end));
|
||||
if overlap == SpanOverlap::NONE && start.is_none() {
|
||||
// if the token is not within the line
|
||||
line_lengths.next();
|
||||
}
|
||||
if overlap == SpanOverlap::START || overlap == SpanOverlap::BOTH {
|
||||
// if the token is within the line
|
||||
start = Some((*line_no, span.start));
|
||||
// we do not need to iterate more.
|
||||
break;
|
||||
}
|
||||
start = Some((*line_no, span.start));
|
||||
// we do not need to iterate more.
|
||||
break;
|
||||
}
|
||||
if overlap == SpanOverlap::END {
|
||||
// if the token is within the line
|
||||
start = Some((*line_no, span.start));
|
||||
// we do not need to iterate more.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +150,7 @@ pub fn to_spans(db: &dyn Db, src: SourceProgram) -> SourceMap {
|
|||
*/
|
||||
spans.push(Spanned::new(
|
||||
db,
|
||||
Span::new(db, (token.start, token.end)),
|
||||
Span::new(db, token.start..token.end),
|
||||
src,
|
||||
Position::new(db, start.0, column),
|
||||
));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
#[cfg(test)]
|
||||
use proptest::{prelude::*};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
#[cfg(test)]
|
||||
use crate::lexer::{Lexer, TokenStreamDisplay};
|
||||
|
||||
use insta::assert_snapshot;
|
||||
use okstd::prelude::*;
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
#[okstd::test]
|
||||
fn test_empty_lexer() {
|
||||
let input = " ";
|
||||
|
|
|
@ -4,10 +4,8 @@ lexer.rs is a lexer for the src language
|
|||
|
||||
use std::{fmt::Display, iter::Iterator, iter::Peekable, str::Chars};
|
||||
|
||||
|
||||
use okstd::prelude::*;
|
||||
|
||||
|
||||
// Identifier
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
pub enum Variable<'input> {
|
||||
|
@ -557,9 +555,9 @@ impl<'input> Lexer<'input> {
|
|||
};
|
||||
Ok(Token::Word(word))
|
||||
}
|
||||
State::String(Quotation) => {
|
||||
State::String(quotation) => {
|
||||
let last_char = self.buffer.chars().last();
|
||||
let quote = if Quotation == Quotation::Double {
|
||||
let quote = if quotation == Quotation::Double {
|
||||
Some('"')
|
||||
} else {
|
||||
Some('\'')
|
||||
|
@ -962,7 +960,9 @@ impl<'input> From<Vec<Spanned<Token<'input>>>> for TokenStreamDisplay<'input> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod lexer_prop_tests;
|
||||
#[cfg(test)]
|
||||
mod lexer_snap_tests;
|
||||
|
||||
pub struct TripleIterator<'input>(Lexer<'input>);
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use proptest::prelude::*;
|
||||
|
||||
pub const ANON_FN_NAME: &str = "anonymous";
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
|
@ -133,21 +131,6 @@ pub enum Operator {
|
|||
Neg,
|
||||
}
|
||||
|
||||
impl Arbitrary for Operator {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
||||
prop_oneof![
|
||||
Just(Operator::Add),
|
||||
Just(Operator::Sub),
|
||||
Just(Operator::Mul),
|
||||
Just(Operator::Div),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Operator {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let op = match self {
|
||||
|
@ -185,4 +168,25 @@ pub struct ImplDef(pub Ident, pub Option<Ident>, pub Block<Box<Expression>>);
|
|||
pub struct Branch(pub Box<Expression>, pub Vec<(Expression, Block<Box<Expression>>)>);
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Module(pub Vec<Box<Expression>>);
|
||||
pub struct Module(pub Vec<Box<Expression>>);
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
use proptest::prelude::*;
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
impl Arbitrary for Operator {
|
||||
type Parameters = ();
|
||||
type Strategy = BoxedStrategy<Self>;
|
||||
|
||||
fn arbitrary_with(_args: ()) -> Self::Strategy {
|
||||
prop_oneof![
|
||||
Just(Operator::Add),
|
||||
Just(Operator::Sub),
|
||||
Just(Operator::Mul),
|
||||
Just(Operator::Div),
|
||||
]
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
|
||||
#[cfg(test)]
|
||||
mod parser_snap_tests;
|
||||
mod string;
|
||||
pub mod ast;
|
||||
|
|
|
@ -1,384 +0,0 @@
|
|||
---
|
||||
source: src/parser/parser_snap_tests.rs
|
||||
expression: "format!(\"{:#?}\", t.unwrap())"
|
||||
---
|
||||
Module(
|
||||
[
|
||||
UseDef(
|
||||
UseDef(
|
||||
[
|
||||
Ident(
|
||||
"crosmvm",
|
||||
None,
|
||||
),
|
||||
],
|
||||
Ident(
|
||||
"std",
|
||||
None,
|
||||
),
|
||||
),
|
||||
),
|
||||
EffectDef(
|
||||
EffectDef(
|
||||
Ident(
|
||||
"Vm",
|
||||
None,
|
||||
),
|
||||
[
|
||||
Ident(
|
||||
"async",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"throws",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"execs",
|
||||
None,
|
||||
),
|
||||
],
|
||||
Block(
|
||||
[
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"catch",
|
||||
None,
|
||||
),
|
||||
args: [],
|
||||
ret: None,
|
||||
effects: [
|
||||
Ident(
|
||||
"throws",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"await",
|
||||
Some(
|
||||
[
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
args: [
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"f",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"Future",
|
||||
Some(
|
||||
[
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret: Some(
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
),
|
||||
effects: [
|
||||
Ident(
|
||||
"async",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"throws",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"exec",
|
||||
None,
|
||||
),
|
||||
args: [
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"arg0",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"string",
|
||||
None,
|
||||
),
|
||||
),
|
||||
),
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"args",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"stringvec",
|
||||
None,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret: Some(
|
||||
Ident(
|
||||
"i32",
|
||||
None,
|
||||
),
|
||||
),
|
||||
effects: [
|
||||
Ident(
|
||||
"Vm",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
StructDef(
|
||||
StructDef(
|
||||
Ident(
|
||||
"coopvm",
|
||||
None,
|
||||
),
|
||||
Block(
|
||||
[],
|
||||
),
|
||||
),
|
||||
),
|
||||
ImplDef(
|
||||
ImplDef(
|
||||
Ident(
|
||||
"Vm",
|
||||
None,
|
||||
),
|
||||
Some(
|
||||
Ident(
|
||||
"coopvm",
|
||||
None,
|
||||
),
|
||||
),
|
||||
Block(
|
||||
[
|
||||
FnDef(
|
||||
FnDef(
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"catch",
|
||||
None,
|
||||
),
|
||||
args: [
|
||||
Reciever,
|
||||
],
|
||||
ret: None,
|
||||
effects: [
|
||||
Ident(
|
||||
"throws",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
Block(
|
||||
[],
|
||||
),
|
||||
[],
|
||||
),
|
||||
),
|
||||
FnDef(
|
||||
FnDef(
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"await",
|
||||
Some(
|
||||
[
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
args: [
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"f",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"Future",
|
||||
Some(
|
||||
[
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret: Some(
|
||||
Ident(
|
||||
"T",
|
||||
None,
|
||||
),
|
||||
),
|
||||
effects: [
|
||||
Ident(
|
||||
"async",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"trhows",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
Block(
|
||||
[
|
||||
FnCall(
|
||||
FnCall(
|
||||
Ident(
|
||||
"yield",
|
||||
None,
|
||||
),
|
||||
[],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
[],
|
||||
),
|
||||
),
|
||||
FnDef(
|
||||
FnDef(
|
||||
Prototype {
|
||||
name: Ident(
|
||||
"exec",
|
||||
None,
|
||||
),
|
||||
args: [
|
||||
Reciever,
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"arg0",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"string",
|
||||
None,
|
||||
),
|
||||
),
|
||||
),
|
||||
Field(
|
||||
Field(
|
||||
Ident(
|
||||
"args",
|
||||
None,
|
||||
),
|
||||
Ident(
|
||||
"vec",
|
||||
Some(
|
||||
[
|
||||
Ident(
|
||||
"string",
|
||||
None,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
ret: Some(
|
||||
Ident(
|
||||
"i32",
|
||||
None,
|
||||
),
|
||||
),
|
||||
effects: [
|
||||
Ident(
|
||||
"Vm",
|
||||
None,
|
||||
),
|
||||
],
|
||||
},
|
||||
Block(
|
||||
[
|
||||
Branch(
|
||||
Branch(
|
||||
FnCall(
|
||||
FnCall(
|
||||
Ident(
|
||||
"self.exec",
|
||||
None,
|
||||
),
|
||||
[
|
||||
Ident(
|
||||
Ident(
|
||||
"arg0",
|
||||
None,
|
||||
),
|
||||
),
|
||||
Ident(
|
||||
Ident(
|
||||
"args",
|
||||
None,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
[
|
||||
(
|
||||
Bool(
|
||||
true,
|
||||
),
|
||||
Block(
|
||||
[
|
||||
FnCall(
|
||||
FnCall(
|
||||
Ident(
|
||||
"raise",
|
||||
None,
|
||||
),
|
||||
[],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
[],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
|
@ -1,6 +1,5 @@
|
|||
---
|
||||
source: src/parser/parser_snap_tests.rs
|
||||
assertion_line: 110
|
||||
expression: "format!(\"{:#?}\", t.unwrap())"
|
||||
---
|
||||
Module(
|
21
tsconfig.json
Normal file
21
tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"lib": ["DOM", "ES2022"],
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"newLine": "lf",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"skipLibCheck": true,
|
||||
// "sourceMap": true,
|
||||
},
|
||||
"exclude": ["."],
|
||||
"ts-node": {
|
||||
"compilerOptions": {
|
||||
"module": "CommonJS",
|
||||
},
|
||||
},
|
||||
}
|
Loading…
Reference in a new issue