mirror of
https://github.com/Strum355/mcshader-lsp.git
synced 2025-08-31 05:47:22 +00:00
Added "Show flattened file" command to display a fully flattened top-level file in vscode virtual doc
execute_command now returns content to the client on success, any valid json value
This commit is contained in:
parent
49324cfb04
commit
26c855f016
8 changed files with 224 additions and 109 deletions
|
@ -1,7 +1,6 @@
|
|||
import * as vscode from 'vscode'
|
||||
import * as lsp from 'vscode-languageclient'
|
||||
import { Extension } from './extension'
|
||||
import { tryInstallExecutable } from './glslangValidator'
|
||||
import { log } from './log'
|
||||
|
||||
export type Command = (...args: any[]) => unknown
|
||||
|
@ -23,8 +22,31 @@ export function restartExtension(e: Extension): Command {
|
|||
}
|
||||
}
|
||||
|
||||
export function downloadValidator(e: Extension): Command {
|
||||
export function virtualMergedDocument(e: Extension): Command {
|
||||
const getVirtualDocument = async (path: string): Promise<string> => {
|
||||
const content = await e.lspClient.sendRequest<string>(lsp.ExecuteCommandRequest.type.method, {
|
||||
command: 'virtualMerge',
|
||||
arguments: [path]
|
||||
})
|
||||
|
||||
return content
|
||||
}
|
||||
|
||||
const docProvider = new class implements vscode.TextDocumentContentProvider {
|
||||
onDidChangeEmitter = new vscode.EventEmitter<vscode.Uri>();
|
||||
onDidChange = this.onDidChangeEmitter.event;
|
||||
|
||||
provideTextDocumentContent(uri: vscode.Uri, __: vscode.CancellationToken): vscode.ProviderResult<string> {
|
||||
return getVirtualDocument(uri.path)
|
||||
}
|
||||
}
|
||||
|
||||
e.context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider('mcglsl', docProvider))
|
||||
|
||||
return async () => {
|
||||
await tryInstallExecutable(e)
|
||||
const uri = vscode.window.activeTextEditor.document.uri
|
||||
const path = vscode.Uri.parse('mcglsl:' + uri.path)
|
||||
const doc = await vscode.workspace.openTextDocument(path)
|
||||
await vscode.window.showTextDocument(doc, {preview: true})
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import * as vscode from 'vscode'
|
||||
import * as lsp from 'vscode-languageclient'
|
||||
import * as commands from './commands'
|
||||
import { bootstrapGLSLangValidator } from './glslangValidator'
|
||||
import { log } from './log'
|
||||
import { LanguageClient } from './lspClient'
|
||||
|
||||
|
@ -23,10 +22,8 @@ export class Extension {
|
|||
|
||||
this.registerCommand('graphDot', commands.generateGraphDot)
|
||||
this.registerCommand('restart', commands.restartExtension)
|
||||
this.registerCommand('downlaod', commands.downloadValidator)
|
||||
this.registerCommand('virtualMerge', commands.virtualMergedDocument)
|
||||
|
||||
if(!await bootstrapGLSLangValidator(this)) return
|
||||
|
||||
log.info('starting language server...')
|
||||
|
||||
this.client = await new LanguageClient(this).startServer()
|
||||
|
|
|
@ -37,11 +37,12 @@
|
|||
},
|
||||
{
|
||||
"command": "mcshader.restart",
|
||||
"title": "Reload Language Server",
|
||||
"title": "Restart Language Server",
|
||||
"category": "Minecraft Shader"
|
||||
},{
|
||||
"command": "mcshader.download",
|
||||
"title": "Download glslangValidator",
|
||||
},
|
||||
{
|
||||
"command": "mcshader.virtualMerge",
|
||||
"title": "Show flattened file",
|
||||
"category": "Minecraft Shader"
|
||||
}
|
||||
],
|
||||
|
|
14
server/Cargo.lock
generated
14
server/Cargo.lock
generated
|
@ -1059,9 +1059,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
|
||||
checksum = "d9251239e129e16308e70d853559389de218ac275b515068abc96829d05b948a"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
@ -1071,9 +1071,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.21"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
|
||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
|
@ -1087,7 +1087,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "rust_lsp"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/Strum355/RustLSP?branch=master#629507c387b479d5bdeb0a4eed9ef9aff34801ce"
|
||||
source = "git+https://github.com/Strum355/RustLSP?branch=master#1f6d533300fd64a739930fd42f0572328be48469"
|
||||
dependencies = [
|
||||
"log",
|
||||
"lsp-types",
|
||||
|
@ -1212,9 +1212,9 @@ checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
|||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.6.0"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a55ca5f3b68e41c979bf8c46a6f1da892ca4db8f94023ce0bd32407573b1ac0"
|
||||
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
|
|
165
server/src/commands.rs
Normal file
165
server/src/commands.rs
Normal file
|
@ -0,0 +1,165 @@
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use petgraph::{dot, graph::NodeIndex};
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use std::fs;
|
||||
|
||||
use crate::{graph::CachedStableGraph, merge_views};
|
||||
use crate::dfs;
|
||||
|
||||
pub struct CustomCommandProvider {
|
||||
commands: HashMap<String, Box<dyn Invokeable>>
|
||||
}
|
||||
|
||||
impl CustomCommandProvider {
|
||||
pub fn new(commands: Vec<(&str, Box<dyn Invokeable>)>) -> CustomCommandProvider {
|
||||
CustomCommandProvider{
|
||||
commands: commands.into_iter().map(|tup| {
|
||||
(tup.0.into(), tup.1)
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&self, command: &str, args: Vec<Value>) -> Result<Value, String> {
|
||||
if self.commands.contains_key(command) {
|
||||
return self.commands.get(command).unwrap().run_command(args);
|
||||
}
|
||||
Err("command doesn't exist".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Invokeable {
|
||||
fn run_command(&self, arguments: Vec<Value>) -> Result<Value, String>;
|
||||
}
|
||||
|
||||
pub struct GraphDotCommand {
|
||||
pub graph: Rc<RefCell<CachedStableGraph>>
|
||||
}
|
||||
|
||||
impl Invokeable for GraphDotCommand {
|
||||
fn run_command(&self, arguments: Vec<Value>) -> Result<Value, String> {
|
||||
let rootpath = arguments.get(0).unwrap().to_string();
|
||||
let rootpath = String::from(rootpath.trim_start_matches('"').trim_end_matches('"'));
|
||||
let filepath = rootpath + "/graph.dot";
|
||||
eprintln!("generating dot file at {}", filepath);
|
||||
let mut file = OpenOptions::new()
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(filepath)
|
||||
.unwrap();
|
||||
|
||||
let mut write_data_closure = || -> Result<(), std::io::Error> {
|
||||
let graph = self.graph.as_ref();
|
||||
|
||||
file.seek(std::io::SeekFrom::Start(0))?;
|
||||
file.write_all(dot::Dot::new(&(graph.borrow().graph)).to_string().as_bytes())?;
|
||||
file.flush()?;
|
||||
file.seek(std::io::SeekFrom::Start(0))?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match write_data_closure() {
|
||||
Err(err) => Err(format!("Error generating graphviz data: {}", err)),
|
||||
_ => Ok(Value::Null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VirtualMergedDocument {
|
||||
pub graph: Rc<RefCell<CachedStableGraph>>
|
||||
}
|
||||
|
||||
impl VirtualMergedDocument {
|
||||
// TODO: DUPLICATE CODE
|
||||
fn get_file_toplevel_ancestors(&self, uri: &str) -> Result<Option<Vec<petgraph::stable_graph::NodeIndex>>> {
|
||||
let curr_node = match self.graph.borrow_mut().find_node(uri) {
|
||||
Some(n) => n,
|
||||
None => return Err(anyhow!("node not found")),
|
||||
};
|
||||
let roots = self.graph.borrow().collect_root_ancestors(curr_node);
|
||||
if roots.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
Ok(Some(roots))
|
||||
}
|
||||
|
||||
pub fn get_dfs_for_node(&self, root: NodeIndex) -> Result<Vec<(NodeIndex, Option<NodeIndex>)>, dfs::error::CycleError> {
|
||||
let graph_ref = self.graph.borrow();
|
||||
|
||||
let dfs = dfs::Dfs::new(&graph_ref, root);
|
||||
|
||||
dfs.collect::<Result<Vec<_>, _>>()
|
||||
}
|
||||
|
||||
pub fn load_sources(&self, nodes: &[(NodeIndex, Option<NodeIndex>)]) -> Result<HashMap<String, String>> {
|
||||
let mut sources = HashMap::new();
|
||||
|
||||
for node in nodes {
|
||||
let graph = self.graph.borrow();
|
||||
let path = graph.get_node(node.0);
|
||||
|
||||
if sources.contains_key(path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let source = fs::read_to_string(path)?;
|
||||
sources.insert(path.clone(), source);
|
||||
}
|
||||
|
||||
Ok(sources)
|
||||
}
|
||||
}
|
||||
|
||||
impl Invokeable for VirtualMergedDocument {
|
||||
fn run_command(&self, arguments: Vec<Value>) -> Result<Value, String> {
|
||||
let path = arguments.get(0).unwrap().to_string();
|
||||
let path = String::from(path.trim_start_matches('"').trim_end_matches('"'));
|
||||
|
||||
let file_ancestors = match self.get_file_toplevel_ancestors(&path) {
|
||||
Ok(opt) => match opt {
|
||||
Some(ancestors) => ancestors,
|
||||
None => vec![],
|
||||
},
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
|
||||
eprintln!("ancestors for {}:\n\t{:?}", path, file_ancestors.iter().map(|e| self.graph.borrow().graph.node_weight(*e).unwrap().clone()).collect::<Vec<String>>());
|
||||
|
||||
// the set of all filepath->content. TODO: change to Url?
|
||||
let mut all_sources: HashMap<String, String> = HashMap::new();
|
||||
|
||||
// if we are a top-level file (this has to be one of the set defined by Optifine, right?)
|
||||
if file_ancestors.is_empty() {
|
||||
// gather the list of all descendants
|
||||
let root = self.graph.borrow_mut().find_node(&path).unwrap();
|
||||
let tree = match self.get_dfs_for_node(root) {
|
||||
Ok(tree) => tree,
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
|
||||
let sources = match self.load_sources(&tree) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(e.to_string())
|
||||
};
|
||||
all_sources.extend(sources);
|
||||
|
||||
let graph = self.graph.borrow();
|
||||
let view = merge_views::generate_merge_list(&tree, &all_sources, &graph);
|
||||
return Ok(serde_json::value::Value::String(view));
|
||||
} else {
|
||||
return Err(format!("{} is not a top-level file aka has ancestors", path))
|
||||
};
|
||||
|
||||
|
||||
//Ok(Value::Null)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use petgraph::stable_graph::NodeIndex;
|
|||
|
||||
use crate::graph::CachedStableGraph;
|
||||
|
||||
use anyhow::{Result, Error};
|
||||
use anyhow::Result;
|
||||
|
||||
struct VisitCount {
|
||||
node: NodeIndex,
|
||||
|
|
|
@ -4,6 +4,7 @@ use rust_lsp::lsp_types::{*, notification::*};
|
|||
|
||||
use petgraph::stable_graph::NodeIndex;
|
||||
|
||||
use serde_json::Value;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
use std::cell::RefCell;
|
||||
|
@ -29,7 +30,7 @@ use regex::Regex;
|
|||
use lazy_static::lazy_static;
|
||||
|
||||
mod graph;
|
||||
mod provider;
|
||||
mod commands;
|
||||
mod lsp_ext;
|
||||
mod dfs;
|
||||
mod merge_views;
|
||||
|
@ -61,12 +62,20 @@ fn main() {
|
|||
command_provider: None,
|
||||
};
|
||||
|
||||
langserver.command_provider = Some(provider::CustomCommandProvider::new(vec![(
|
||||
langserver.command_provider = Some(commands::CustomCommandProvider::new(vec![
|
||||
(
|
||||
"graphDot",
|
||||
Box::new(provider::GraphDotCommand {
|
||||
Box::new(commands::GraphDotCommand {
|
||||
graph: Rc::clone(&langserver.graph),
|
||||
}),
|
||||
)]));
|
||||
),
|
||||
(
|
||||
"virtualMerge",
|
||||
Box::new(commands::VirtualMergedDocument{
|
||||
graph: Rc::clone(&langserver.graph)
|
||||
})
|
||||
)
|
||||
]));
|
||||
|
||||
LSPEndpoint::run_server_from_input(&mut stdin.lock(), endpoint_output, langserver);
|
||||
}
|
||||
|
@ -76,7 +85,7 @@ struct MinecraftShaderLanguageServer {
|
|||
graph: Rc<RefCell<graph::CachedStableGraph>>,
|
||||
wait: WaitGroup,
|
||||
root: String,
|
||||
command_provider: Option<provider::CustomCommandProvider>,
|
||||
command_provider: Option<commands::CustomCommandProvider>,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -596,27 +605,18 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
|
|||
})); */
|
||||
}
|
||||
|
||||
fn execute_command(&mut self, mut params: ExecuteCommandParams, completable: LSCompletable<WorkspaceEdit>) {
|
||||
params
|
||||
.arguments
|
||||
.push(serde_json::Value::String(self.root.clone()));
|
||||
match self
|
||||
.command_provider
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.execute(params.command.as_ref(), params.arguments)
|
||||
fn execute_command(&mut self, mut params: ExecuteCommandParams, completable: LSCompletable<Option<Value>>) {
|
||||
params.arguments.push(serde_json::Value::String(self.root.clone()));
|
||||
|
||||
match self.command_provider.as_ref().unwrap().execute(¶ms.command, params.arguments)
|
||||
{
|
||||
Ok(_) => {
|
||||
Ok(resp) => {
|
||||
eprintln!("executed {} successfully", params.command);
|
||||
self.endpoint.send_notification(ShowMessage::METHOD, ShowMessageParams {
|
||||
typ: MessageType::Info,
|
||||
message: format!("Command {} executed successfully.", params.command),
|
||||
}).expect("failed to send popup/show message notification");
|
||||
completable.complete(Ok(WorkspaceEdit {
|
||||
changes: None,
|
||||
document_changes: None,
|
||||
change_annotations: Option::None,
|
||||
}))
|
||||
completable.complete(Ok(Some(resp)))
|
||||
},
|
||||
Err(err) => {
|
||||
self.endpoint.send_notification(ShowMessage::METHOD, ShowMessageParams {
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::cell::RefCell;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::prelude::*;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use petgraph::dot;
|
||||
|
||||
use crate::graph::CachedStableGraph;
|
||||
|
||||
pub struct CustomCommandProvider {
|
||||
commands: HashMap<String, Box<dyn Invokeable>>
|
||||
}
|
||||
|
||||
impl CustomCommandProvider {
|
||||
pub fn new(commands: Vec<(&str, Box<dyn Invokeable>)>) -> CustomCommandProvider {
|
||||
CustomCommandProvider{
|
||||
commands: commands.into_iter().map(|tup| {
|
||||
(tup.0.into(), tup.1)
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute(&self, command: &str, args: Vec<Value>) -> Result<(), String> {
|
||||
if self.commands.contains_key(command) {
|
||||
return self.commands.get(command).unwrap().run_command(args);
|
||||
}
|
||||
Err("command doesn't exist".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Invokeable {
|
||||
fn run_command(&self, arguments: Vec<Value>) -> Result<(), String>;
|
||||
}
|
||||
|
||||
pub struct GraphDotCommand {
|
||||
pub graph: Rc<RefCell<CachedStableGraph>>
|
||||
}
|
||||
|
||||
impl<'a> Invokeable for GraphDotCommand {
|
||||
fn run_command(&self, params: Vec<Value>) -> Result<(), String> {
|
||||
let rootpath = params.get(0).unwrap().to_string();
|
||||
let rootpath = String::from(rootpath.trim_start_matches('"').trim_end_matches('"'));
|
||||
let filepath = rootpath + "/graph.dot";
|
||||
eprintln!("generating dot file at {}", filepath);
|
||||
let mut file = OpenOptions::new()
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(filepath)
|
||||
.unwrap();
|
||||
|
||||
let mut write_data_closure = || -> Result<(), std::io::Error> {
|
||||
let graph = self.graph.as_ref();
|
||||
|
||||
file.seek(std::io::SeekFrom::Start(0))?;
|
||||
file.write_all(dot::Dot::new(&(graph.borrow().graph)).to_string().as_bytes())?;
|
||||
file.flush()?;
|
||||
file.seek(std::io::SeekFrom::Start(0))?;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
match write_data_closure() {
|
||||
Err(err) => Err(format!("Error generating graphviz data: {}", err)),
|
||||
_ => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue