mirror of
https://github.com/Strum355/mcshader-lsp.git
synced 2025-09-01 22:37:41 +00:00
Status bar items and glslangValidator download
glslangValidator download done in client side both client and server create status bar items to notify of progress etc
This commit is contained in:
parent
46215f8a91
commit
36fd728533
3 changed files with 189 additions and 55 deletions
|
@ -1,40 +1,72 @@
|
|||
import * as path from 'path'
|
||||
import * as vscode from 'vscode'
|
||||
import * as vscodeLang from 'vscode-languageclient'
|
||||
import { promptDownload, testExecutable } from './glslangValidator'
|
||||
|
||||
export const glslConfigParam = 'mcglsl.glslangValidatorPath'
|
||||
|
||||
export let outputChannel: vscode.OutputChannel
|
||||
|
||||
let statusBarItem: vscode.StatusBarItem
|
||||
|
||||
let globalContext: vscode.ExtensionContext
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
const outputChannel = vscode.window.createOutputChannel('vscode-mc-shader')
|
||||
outputChannel = vscode.window.createOutputChannel('vscode-mc-shader')
|
||||
globalContext = context
|
||||
|
||||
const clientOpts: vscodeLang.LanguageClientOptions = {
|
||||
documentSelector: [{scheme: 'file', language: 'glsl'}],
|
||||
outputChannel: outputChannel,
|
||||
outputChannelName: 'vscode-mc-shader',
|
||||
synchronize: {
|
||||
configurationSection: 'mcglsl',
|
||||
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{fsh,gsh,vsh,glsl}')
|
||||
},
|
||||
{
|
||||
if (!testExecutable(vscode.workspace.getConfiguration().get(glslConfigParam))) {
|
||||
await promptDownload()
|
||||
} else {
|
||||
outputChannel.appendLine('glslangValidator found!')
|
||||
}
|
||||
}
|
||||
|
||||
const serverOpts: vscodeLang.ServerOptions = {
|
||||
command: context.asAbsolutePath(path.join('server', 'target', 'debug', 'vscode-mc-shader')),
|
||||
|
||||
}
|
||||
|
||||
outputChannel.appendLine('starting language server...')
|
||||
|
||||
const langServer = new vscodeLang.LanguageClient('vscode-mc-shader', serverOpts, clientOpts)
|
||||
|
||||
context.subscriptions.push(langServer.start())
|
||||
|
||||
await langServer.onReady()
|
||||
{
|
||||
|
||||
langServer.onNotification('sampleText', (...nums: number[]) => {
|
||||
outputChannel.appendLine(`got notif: ${nums.join(' ')}`)
|
||||
})
|
||||
const clientOpts: vscodeLang.LanguageClientOptions = {
|
||||
documentSelector: [{scheme: 'file', language: 'glsl'}],
|
||||
outputChannel: outputChannel,
|
||||
outputChannelName: 'vscode-mc-shader',
|
||||
synchronize: {
|
||||
configurationSection: 'mcglsl',
|
||||
fileEvents: vscode.workspace.createFileSystemWatcher('**/*.{fsh,gsh,vsh,glsl}')
|
||||
},
|
||||
}
|
||||
|
||||
langServer.onNotification('update-config', (dir: string) => {
|
||||
vscode.workspace.getConfiguration().update('mcglsl.glslangValidatorPath', dir, vscode.ConfigurationTarget.Global)
|
||||
})
|
||||
const serverOpts: vscodeLang.ServerOptions = {
|
||||
command: context.asAbsolutePath(path.join('server', 'target', 'debug', 'vscode-mc-shader')),
|
||||
}
|
||||
|
||||
outputChannel.appendLine('language server started!')
|
||||
outputChannel.appendLine('starting language server...')
|
||||
|
||||
const langServer = new vscodeLang.LanguageClient('vscode-mc-shader', serverOpts, clientOpts)
|
||||
|
||||
context.subscriptions.push(langServer.start())
|
||||
|
||||
await langServer.onReady()
|
||||
|
||||
langServer.onNotification('updateConfig', (dir: string) => {
|
||||
vscode.workspace.getConfiguration().update(glslConfigParam, dir, vscode.ConfigurationTarget.Global)
|
||||
})
|
||||
|
||||
langServer.onNotification('status', updateStatus)
|
||||
|
||||
langServer.onNotification('clearStatus', clearStatus)
|
||||
|
||||
outputChannel.appendLine('language server started!')
|
||||
}
|
||||
}
|
||||
|
||||
export function updateStatus(icon: string, text: string) {
|
||||
if(statusBarItem != null) statusBarItem.dispose()
|
||||
statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left)
|
||||
statusBarItem.text = icon + " [Minecraft Shaders] " + text
|
||||
statusBarItem.show()
|
||||
globalContext.subscriptions.push(statusBarItem)
|
||||
}
|
||||
|
||||
export function clearStatus() {
|
||||
if(statusBarItem != null) statusBarItem.dispose()
|
||||
}
|
89
client/src/glslangValidator.ts
Normal file
89
client/src/glslangValidator.ts
Normal file
|
@ -0,0 +1,89 @@
|
|||
import * as unzip from 'adm-zip'
|
||||
import { execSync } from 'child_process'
|
||||
import { writeFileSync } from 'fs'
|
||||
import fetch from 'node-fetch'
|
||||
import { platform } from 'os'
|
||||
import * as vscode from 'vscode'
|
||||
import { clearStatus, glslConfigParam, outputChannel, updateStatus } from './extension'
|
||||
|
||||
const url = {
|
||||
'win32': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-windows-x64-Release.zip',
|
||||
'linux': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip',
|
||||
'darwin': 'https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-osx-Release.zip'
|
||||
}
|
||||
|
||||
const config = vscode.workspace.getConfiguration()
|
||||
|
||||
export async function promptDownload() {
|
||||
const chosen = await vscode.window.showErrorMessage(
|
||||
`[mc-glsl] glslangValidator not found at: '${config.get(glslConfigParam)}'.`,
|
||||
{title: 'Download'},
|
||||
{title: 'Cancel'}
|
||||
)
|
||||
|
||||
if (!chosen || chosen.title !== 'Download') return
|
||||
|
||||
await installExecutable()
|
||||
}
|
||||
|
||||
async function installExecutable() {
|
||||
try {
|
||||
updateStatus('$(cloud-download)', 'Downloading glslangValidator')
|
||||
|
||||
const glslangBin = '/glslangValidator' + (platform() === 'win32' ? '.exe' : '')
|
||||
const glslangPath = config.get('mcglsl.shaderpacksPath') + glslangBin
|
||||
|
||||
const response = await fetch(url[platform()])
|
||||
outputChannel.appendLine('glslangValidator download response status: ' + response.status )
|
||||
|
||||
const zip = new unzip(await response.buffer())
|
||||
|
||||
const bin = zip.readFile('bin' + glslangBin)
|
||||
outputChannel.appendLine('buffer length ' + bin.length)
|
||||
writeFileSync(glslangPath, bin, {encoding: null, mode: 0o755})
|
||||
|
||||
// Make sure download was successful
|
||||
if (!testExecutable(glslangPath)) {
|
||||
vscode.window.showErrorMessage(`Unexpected error occurred checking for binary at ${glslangPath}. Please try again`)
|
||||
clearStatus()
|
||||
throw new Error('failed to install glslangValidator')
|
||||
}
|
||||
|
||||
// All done!
|
||||
outputChannel.appendLine(`successfully downloaded glslangValidator to ${glslangPath}`)
|
||||
vscode.window.showInformationMessage(
|
||||
`glslangValidator has been downloaded to ${glslangPath}. Your config should be updated automatically.`
|
||||
)
|
||||
config.update('mcglsl.glslangValidatorPath', glslangPath, vscode.ConfigurationTarget.Global)
|
||||
clearStatus()
|
||||
} catch (e) {
|
||||
outputChannel.appendLine(`failed downloading glslangValidator ${e}`)
|
||||
vscode.window.showErrorMessage(`Failed to install glslangValidator: ${e}`)
|
||||
clearStatus()
|
||||
throw e
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function testExecutable(glslangPath?: string): boolean {
|
||||
glslangPath = glslangPath || config.get(glslConfigParam)
|
||||
let stdout = ''
|
||||
try {
|
||||
stdout = execSync(glslangPath, {
|
||||
stdio: 'pipe',
|
||||
}).toString()
|
||||
} catch (e) {
|
||||
stdout = (e.stdout.toString() as string)
|
||||
}
|
||||
|
||||
outputChannel.appendLine('glslangValidator first line stdout: "' + stdout.split('\n')[0] + '"')
|
||||
const success = stdout.startsWith('Usage')
|
||||
|
||||
if (success) {
|
||||
outputChannel.appendLine(`glslangValidator found at ${glslangPath}`)
|
||||
} else {
|
||||
outputChannel.appendLine(`glslangValidator not found at ${glslangPath}`)
|
||||
}
|
||||
|
||||
return success
|
||||
}
|
|
@ -67,37 +67,39 @@ impl MinecraftShaderLanguageServer {
|
|||
}
|
||||
|
||||
pub fn gen_initial_graph(&mut self, root: String) {
|
||||
self.endpoint.send_notification("status", vec!["$(loading~spin)", "Building project..."]).unwrap();
|
||||
let mut files = HashMap::new();
|
||||
|
||||
eprintln!("root of project is {}", root);
|
||||
for entry_res in walkdir::WalkDir::new(root.clone()).into_iter() {
|
||||
let entry = match entry_res {
|
||||
Ok(entry) => entry,
|
||||
Err(e) => {
|
||||
eprintln!("error {} {:?}", e.path().unwrap_or(Path::new("")).display(), e);
|
||||
break;
|
||||
},
|
||||
};
|
||||
|
||||
// filter directories and files not ending in any of the 3 extensions
|
||||
let file_iter = walkdir::WalkDir::new(root.clone()).into_iter().filter_map(|entry| {
|
||||
if !entry.is_ok() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let entry = entry.unwrap();
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_dir() {
|
||||
continue;
|
||||
return None;
|
||||
}
|
||||
|
||||
if !path.is_dir() {
|
||||
let ext = path.extension().unwrap().to_str().unwrap();
|
||||
if ext != "vsh" && ext != "fsh" && ext != "glsl" {
|
||||
continue;
|
||||
}
|
||||
let ext = path.extension().unwrap().to_str().unwrap();
|
||||
if ext != "vsh" && ext != "fsh" && ext != "glsl" {
|
||||
return None;
|
||||
}
|
||||
Some(String::from(path.to_str().unwrap()))
|
||||
});
|
||||
|
||||
let includes = self.find_includes(root.as_str(), path.to_str().unwrap());
|
||||
// iterate all valid found files, search for includes, add a node into the graph for each
|
||||
// file and add a file->includes KV into the map
|
||||
for entry_res in file_iter {
|
||||
let includes = self.find_includes(root.as_str(), entry_res.as_str());
|
||||
|
||||
let stripped_path = String::from(String::from(path.to_str().unwrap()).trim_start_matches(root.as_str()));
|
||||
let stripped_path = String::from(String::from(entry_res));
|
||||
let idx = self.graph.add_node(stripped_path.clone());
|
||||
|
||||
eprintln!("adding {} with\n{:?}", stripped_path.clone(), includes);
|
||||
//eprintln!("adding {} with\n{:?}", stripped_path.clone(), includes);
|
||||
|
||||
files.insert(stripped_path, GLSLFile{
|
||||
idx: idx, includes: includes,
|
||||
|
@ -105,20 +107,30 @@ impl MinecraftShaderLanguageServer {
|
|||
}
|
||||
|
||||
// Add edges between nodes, finding target nodes on weight (value)
|
||||
for (k, v) in files.into_iter() {
|
||||
for (_, v) in files.into_iter() {
|
||||
for file in v.includes {
|
||||
let mut iter = self.graph.node_indices();
|
||||
eprintln!("searching for {}", file);
|
||||
let idx = iter.find(|i| self.graph[*i] == file).unwrap();
|
||||
//eprintln!("searching for {}", file);
|
||||
let idx = iter.find(|i| self.graph[*i] == file);
|
||||
if idx.is_none() {
|
||||
eprintln!("couldn't find {} in graph", file);
|
||||
continue;
|
||||
}
|
||||
//eprintln!("added edge between\n\t{}\n\t{}", k, file);
|
||||
self.graph.add_edge(v.idx, idx, String::from("includes"));
|
||||
self.graph.add_edge(v.idx, idx.unwrap(), String::from("includes"));
|
||||
}
|
||||
}
|
||||
|
||||
self.file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
self.file.write_all(dot::Dot::new(&self.graph).to_string().as_bytes()).unwrap();
|
||||
self.file.flush().unwrap();
|
||||
self.file.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||
/* self.file.seek(std::io::SeekFrom::Start(0))?;
|
||||
self.file.write_all(dot::Dot::new(&self.graph).to_string().as_bytes())?;
|
||||
self.file.flush()?;
|
||||
self.file.seek(std::io::SeekFrom::Start(0))?; */
|
||||
|
||||
eprintln!("finished building project include graph");
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
self.endpoint.send_notification("status", vec!["$(check)", "Finished building project!"]).unwrap();
|
||||
std::thread::sleep(std::time::Duration::from_secs(3));
|
||||
self.endpoint.send_notification("clearStatus", None::<()>).unwrap();
|
||||
}
|
||||
|
||||
pub fn find_includes(&self, root: &str, file: &str) -> Vec<String> {
|
||||
|
@ -157,7 +169,8 @@ impl LanguageServerHandling for MinecraftShaderLanguageServer {
|
|||
|
||||
fn workspace_change_configuration(&mut self, _: DidChangeConfigurationParams) {}
|
||||
fn did_open_text_document(&mut self, _: DidOpenTextDocumentParams) {}
|
||||
fn did_change_text_document(&mut self, _: DidChangeTextDocumentParams) {}
|
||||
fn did_change_text_document(&mut self, params: DidChangeTextDocumentParams) {
|
||||
}
|
||||
fn did_close_text_document(&mut self, _: DidCloseTextDocumentParams) {}
|
||||
fn did_save_text_document(&mut self, _: DidSaveTextDocumentParams) {}
|
||||
fn did_change_watched_files(&mut self, _: DidChangeWatchedFilesParams) {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue