Skip to content

UI Updates #15

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Sep 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ docker pull salaheldin18/solang-playground-amd64
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ----------- |
| Compile Solang language server to WASM, and integrate it to a Monaco editor. | Allow editing Solidity source files in the browser, with smarts provided from the server (Diagnostics, code completion, etc..) | Completed |
| Host Solang on a backend service | Allow compiling smart contracts on the web editor | Completed |
| Support Polkadot API | Allow the deployment and interaction with Solidity contracts on Polkadot | In progress |
| IDE Improvements | Improve developer experience when trying out the IDE, making it a more attractive option for Solidity devs | Not started |
| Support Polkadot API | Allow the deployment and interaction with Solidity contracts on Polkadot | Completed |
| IDE Improvements | Improve developer experience when trying out the IDE, making it a more attractive option for Solidity devs | In progress |

## Acknowledgments

Expand Down
1 change: 1 addition & 0 deletions crates/backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ tokio = { version = "1.28.2", features = [
] }
anyhow = "1.0"
log = "0.4"
regex = "1.10"

typescript-type-def = "0.5"
actix-web = "4.5"
Expand Down
12 changes: 12 additions & 0 deletions crates/backend/src/services/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,24 @@ pub enum CompilationResult {
Success {
/// The compiled contract
wasm: Vec<u8>,
/// The standard output of the docker command
stdout: String,
/// The standard error of the docker command
stderr: String,
/// The standard output of the compile command
compile_stdout: String,
/// The standard error of the compile command
compile_stderr: String,
},
Error {
/// The standard output of the docker command
stdout: String,
/// The standard error of the docker command
stderr: String,
/// The standard output of the compile command
compile_stdout: String,
/// The standard error of the compile command
compile_stderr: String,
},
}

Expand Down
83 changes: 75 additions & 8 deletions crates/backend/src/services/sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub fn build_compile_command(input_file: &Path, output_dir: &Path) -> Command {
// Building the compile command
let remove_command = format!("rm -rf {}*.wasm {}*.contract", DOCKER_OUTPUT, DOCKER_OUTPUT);
let compile_command = format!(
"solang compile --target polkadot -o /playground-result {} 2>&1",
"solang compile --target polkadot -o /playground-result {} > /playground-result/stdout.log 2> /playground-result/stderr.log",
file_name
);
let sh_command = format!("{} && {}", remove_command, compile_command);
Expand All @@ -96,7 +96,7 @@ impl Sandbox {
/// Creates a new sandbox
pub fn new() -> Result<Self> {
let scratch = TempDir::with_prefix("solang_playground").context("failed to create scratch directory")?;
let input_file = scratch.path().join("input.rs");
let input_file = scratch.path().join("input.sol");
let output_dir = scratch.path().join("output");
fs::create_dir(&output_dir).context("failed to create output directory")?;

Expand Down Expand Up @@ -124,16 +124,62 @@ impl Sandbox {
.map(|entry| entry.path())
.find(|path| path.extension() == Some(OsStr::new("contract")));

// The file `stdout.log` is in the same directory as the contract file
let compile_log_stdout_file_path = fs::read_dir(&self.output_dir)
.context("failed to read output directory")?
.flatten()
.find(|entry| entry.file_name() == "stdout.log")
.map(|entry| entry.path());

// The file `stderr.log` is in the same directory as the contract file
let compile_log_stderr_file_path = fs::read_dir(&self.output_dir)
.context("failed to read output directory")?
.flatten()
.find(|entry| entry.file_name() == "stderr.log")
.map(|entry| entry.path());

let compile_stdout = match compile_log_stdout_file_path {
Some(path) => fs::read_to_string(&path).context("failed to read compile stdout")?,
None => "No stdout.log file found".to_string(),
};

let compile_stderr = match compile_log_stderr_file_path {
Some(path) => fs::read_to_string(&path).context("failed to read compile stderr")?,
None => "No stderr.log file found".to_string(),
};
let compile_stderr = extract_error_message(&compile_stderr);

let stdout = String::from_utf8(output.stdout).context("failed to convert vec to string")?;
let stderr = String::from_utf8(output.stderr).context("failed to convert vec to string")?;

let compile_response = match file {
Some(file) => match read(&file) {
Ok(Some(wasm)) => CompilationResult::Success { wasm, stderr, stdout },
Ok(None) => CompilationResult::Error { stderr, stdout },
Err(_) => CompilationResult::Error { stderr, stdout },
Ok(Some(wasm)) => CompilationResult::Success {
wasm,
stderr,
stdout,
compile_stdout,
compile_stderr,
},
Ok(None) => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
Err(_) => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
},
None => CompilationResult::Error {
stderr,
stdout,
compile_stdout,
compile_stderr,
},
None => CompilationResult::Error { stderr, stdout },
};

Ok(compile_response)
Expand Down Expand Up @@ -175,9 +221,8 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {
let timeout = TIMEOUT;
println!("executing command!");
let output = command.output().await.context("failed to start compiler")?;
println!("Done! {:?}", output);
let stdout = String::from_utf8_lossy(&output.stdout);

let stdout = String::from_utf8_lossy(&output.stdout);
let id = stdout.lines().next().context("missing compiler ID")?.trim();
let stderr = &output.stderr;

Expand Down Expand Up @@ -210,3 +255,25 @@ async fn run_command(mut command: Command) -> Result<std::process::Output> {

Ok(output)
}

pub fn extract_error_message(log: &str) -> String {
// Remove ANSI escape codes (used for terminal colors)
let cleaned_log = remove_ansi_escape_codes(log);

// Find the start of the actual error message by looking for the keyword "error:"
if let Some(start) = cleaned_log.find("error:") {
// Extract the error message starting from the keyword "error:" but ignore the keyword itself
let error_message = &cleaned_log[start + "error:".len()..];
error_message.to_string()
} else {
// If no error message is found, return a default message
"No error message found".to_string()
}
}

/// Helper function to remove ANSI escape codes from a string
fn remove_ansi_escape_codes(log: &str) -> String {
// Use a regex pattern to remove ANSI escape codes
let re = regex::Regex::new(r"\x1B\[[0-9;]*[a-zA-Z]").unwrap();
re.replace_all(log, "").to_string()
}
8 changes: 1 addition & 7 deletions crates/browser/tests/server_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,17 @@ use solang::languageserver::{Files, GlobalCache, SolangServer};
use std::collections::HashMap;
use std::str::FromStr;
use tokio::sync::Mutex;
use tower_lsp::lsp_types::{
Diagnostic, DiagnosticSeverity,Position, Range,
};
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity, Position, Range};
use tower_lsp::LspService;
use tower_test::mock::Spawn;


use solang::Target;

#[cfg(test)]
mod tests {

use super::*;



const INITIALIZE_REQUEST: &'static str = r#"{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"processId":null,"clientInfo":{"name":"demo-language-client"},"capabilities":{},"rootUri":null}}"#;
const INITIALIZE_EXPECTED_RESPONSE: &'static str = r#"{"jsonrpc":"2.0","result":{"capabilities":{"completionProvider":{"resolveProvider":false,"triggerCharacters":["."]},"declarationProvider":true,"definitionProvider":true,"documentFormattingProvider":true,"executeCommandProvider":{"commands":[]},"hoverProvider":true,"implementationProvider":true,"referencesProvider":true,"renameProvider":true,"signatureHelpProvider":{},"textDocumentSync":2,"typeDefinitionProvider":true,"workspace":{"workspaceFolders":{"changeNotifications":true,"supported":true}},"workspaceSymbolProvider":true}},"id":1}"#;

Expand All @@ -29,7 +24,6 @@ mod tests {

const CHANGE_DOCUMENT_REQUEST: &str = r#"{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"version":0,"uri":"inmemory://demo.js"},"contentChanges":[{"range":{"start":{"line":0,"character":0},"end":{"line":11,"character":0}},"text":" // SPDX-License-Identifier: MIT\n pragma solidity >=0.6.12 <0.9.0;\n contract HelloWorld {\n uint sesa;\n /**\n * @dev Prints Hello World string\n */\n function print() public pure returns (string memory) {\n return \"Hello World!\";\n }\n }\n"}]}}"#;


fn create_mock_service() -> Spawn<LspService<SolangServer>> {
let importpaths = Vec::new();
let importmaps = Vec::new();
Expand Down
13 changes: 5 additions & 8 deletions crates/solang/src/languageserver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2065,10 +2065,7 @@ impl LanguageServer for SolangServer {

async fn initialized(&self, _: InitializedParams) {
self.client
.log_message(
MessageType::INFO,
format!("solang language server {} initialized", "solang"),
)
.log_message(MessageType::INFO, format!("Solang language server initialized"))
.await;
}

Expand All @@ -2078,24 +2075,24 @@ impl LanguageServer for SolangServer {

async fn did_change_workspace_folders(&self, _: DidChangeWorkspaceFoldersParams) {
self.client
.log_message(MessageType::INFO, "workspace folders changed!")
.log_message(MessageType::INFO, "Workspace folders changed!")
.await;
}

async fn did_change_configuration(&self, _: DidChangeConfigurationParams) {
self.client
.log_message(MessageType::INFO, "configuration changed!")
.log_message(MessageType::INFO, "Configuration changed!")
.await;
}

async fn did_change_watched_files(&self, _: DidChangeWatchedFilesParams) {
self.client
.log_message(MessageType::INFO, "watched files have changed!")
.log_message(MessageType::INFO, "Watched files have changed!")
.await;
}

async fn execute_command(&self, _: ExecuteCommandParams) -> Result<Option<Value>> {
self.client.log_message(MessageType::INFO, "command executed!").await;
self.client.log_message(MessageType::INFO, "Command executed!").await;
Ok(None)
}

Expand Down
135 changes: 84 additions & 51 deletions packages/app/assets/index.html
Original file line number Diff line number Diff line change
@@ -1,55 +1,88 @@
<!DOCTYPE html>
<html>
<head>
<style>
body, html {
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
#container {
display: flex;
flex-direction: column;
height: 100%;
}
#cell-editor {
flex-grow: 1;
min-height: 0;
}
#editor {
width: 100%;
height: 100%;
overflow: auto;
}
textarea {
width: 100%;
resize: none;
}
</style>
</head>
<body>
<div id="container">
<div id="buttonContainer">
<button id="compile">Compile for Polkadot Target</button>
<button id="interact">Deploy/Interact with Compiled Contracts on chain</button>
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css" />
<style>
.button-12 {
display: flex;
flex-direction: row;
align-items: center;
padding: 8px 16px;
font-family: -apple-system, BlinkMacSystemFont, "Roboto", sans-serif;
font-size: 16px;
font-weight: 500;
border-radius: 8px;
border: none;
background-color: #1e1e1e;
outline: none;
border: 0;

color: #dfdedf;
user-select: none;
-webkit-user-select: none;
touch-action: manipulation;
cursor: pointer;
transition: background 0.3s ease;
}

.button-12 i {
margin-right: 8px;
font-size: 18px;
}

.button-12:focus {
box-shadow: inset 0px 1px 0px -0.25px rgba(255, 255, 255, 0.2), 0px 1px 2px rgba(0, 0, 0, 0.1),
0px 0px 0px 3.5px rgba(58, 108, 217, 0.5);
outline: 0;
}

.button-12:hover {
background: #383838;
color: #dfdedf;
}
</style>
</head>
<body>
<div id="top-bar">
<div id="left-section">
<div style="display: flex">
<div style="width: 160px; margin-right: 16px; margin-left: 16px">
<img
id="logo"
src="https://raw.githubusercontent.com/hyperledger/solang/main/docs/hl_solang_horizontal-color.svg"
alt="Solang Logo"
/>
</div>
<div style="display: flex; gap: 12px">
<button class="button-12" id="compile"><i class="fas fa-rocket"></i> Compile for Polkadot Target</button>
<button class="button-12" id="interact">
<i class="fas fa-network-wired"></i> Deploy/Interact with Compiled Contracts on Chain
</button>
</div>
</div>
<div style="display: flex; gap: 12px; margin-right: 16px">
<button class="button-12" id="docs"><i class="fas fa-book"></i> Solang Docs</button>
<button class="button-12" id="github"><i class="fab fa-github"></i> GitHub Repo</button>
</div>
</div>
</div>
<div id="cell-editor">
<label for="editor">editor</label>
<div id="editor"></div>
<div id="container">
<div id="cell-editor">
<div id="editor"></div>
</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>
<!--Note: Remove "display: none" to show the message trace (For debugging purposes) -->
<div id="cell-client" style="display: none">
<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" style="display: none">
<label for="channel-server">message trace (client ⇐ server)</label>
<textarea id="channel-server" autocomplete="off" spellcheck="off" wrap="off" readonly rows="8"></textarea>
</div>
</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="8"></textarea>
</div>
</div>
</body>
</html>
</body>
</html>
Loading
Loading