'use strict' ; import * as path from "path" ; import { ServerOptions , TransportKind , LanguageClientOptions , LanguageClient } from "vscode-languageclient" ; import { OutputChannel , WorkspaceFolder , TextDocument , workspace , ExtensionContext , window , StatusBarItem , StatusBarAlignment , env , commands , Uri } from "vscode" ; import { existsSync , readFileSync } from "fs" ; import { URL } from "url" ; import { execFile , ChildProcess } from "child_process" ; import URI from "vscode-uri" ; /**
 * 每个工作空间一个后端服务
 */ let clients = new Map ( ) ; let serverModule ; let debugPort = 6009 ; let outputChannel ; let statusBars = [ ] ; let quickRoot ; let playerProcess ; param folder function createClient ( folder ) { debugPort ++ ; let debugOptions = { execArgv : [ "--nolazy" , "--inspect=" + debugPort ] } ; let serverOptions = { run : { module : serverModule , transport : TransportKind . ipc } , debug : { module : serverModule , transport : TransportKind . ipc , options : debugOptions } } ; let clientOptions = { outputChannel : outputChannel , documentSelector : [ { scheme : 'file' , language : 'lua' } ] , workspaceFolder : folder , synchronize : { configurationSection : 'quickxdev' , fileEvents : workspace . createFileSystemWatcher ( '**/.clientrc' ) } , } ; let client = new LanguageClient ( 'QuickXDev' , 'QuickXDevServer' , serverOptions , clientOptions ) ; client . onReady ( ) . then ( ( ) => { client . onRequest ( "showStatusBar" , ( message ) => { let statusBar = window . createStatusBarItem ( StatusBarAlignment . Left , 0 ) ; if ( message . color != "" ) { statusBar . color = message . color ; } statusBar . text = message . text ; statusBar . show ( ) ; statusBars . push ( statusBar ) ; } ) ; client . onRequest ( "hideStatusBar" , ( ) => { for ( let i = 0 ; i < statusBars . length ; i ++ ) { statusBars [ i ] . dispose ( ) ; } statusBars = [ ] ; } ) ; client . onRequest ( "quickRoot" , ( text ) => { quickRoot = text ; } ) ; client . onRequest ( "getDocLine" , ( params ) : string => { let activeDoc = window . activeTextEditor . document ; if ( activeDoc . fileName != params . filepath || activeDoc . lineCount < params . line ) { return "" ; } return activeDoc . lineAt ( params . line ) . text ; } ) ; } ) ; client . start ( ) ; clients . set ( folder . uri . toString ( ) , client ) ; } param document function didOpenTextDocument ( document ) { if ( document . languageId !== "lua" || document . uri . scheme !== "file" ) { return ; } let folder = workspace . getWorkspaceFolder ( document . uri ) ; if ( ! folder ) { return ; } if ( ! clients . get ( folder . uri . toString ( ) ) ) { createClient ( folder ) ; } } /**
 * 运行Player
 */ function runPlayer ( ) { if ( playerProcess ) { playerProcess . kill ( ) ; playerProcess = null ; } if ( ! quickRoot ) { window . showErrorMessage ( "没有设置QuickX目录" ) ; return ; } let activeFile = window . activeTextEditor . document . fileName ; let workDirUri = workspace . getWorkspaceFolder ( URI . file ( activeFile ) ) . uri ; let client = clients . get ( workDirUri . toString ( ) ) ; if ( ! client ) { return ; } let isVersion4 = false ; let gameRunnerFolder = quickRoot + "/tools/runner/" ; if ( existsSync ( new URL ( gameRunnerFolder ) ) ) { isVersion4 = true ; } client . sendRequest ( "isQuickX" ) . then ( ( isQuickX ) => { if ( ! isQuickX ) { let msg = "非 quick 项目下不能运行 Player！" ; if ( isVersion4 ) { msg = "非 quick 项目下不能运行 LuaGameRunner！" ; } window . showErrorMessage ( msg ) ; return ; } if ( ! isVersion4 ) { doRunPlayer ( workDirUri ) ; } else { doRunGameRunner ( workDirUri ) ; } } ) ; } function doRunPlayer ( workDirUri ) { let playerPath = quickRoot + "/quick/player/" ; if ( process . platform == "win32" ) { playerPath += "win32/player3.exe" ; } else if ( process . platform == "darwin" ) { playerPath += "player3.app/Contents/MacOS/player3" ; } else { window . showErrorMessage ( "抱歉，不支持当前制作系统!" ) ; return ; } if ( ! existsSync ( new URL ( playerPath ) ) ) { window . showErrorMessage ( "Player不存在" ) ; return ; } let playerFsPath = URI . parse ( playerPath ) . fsPath ; let args = [ ] ; args . push ( "-workdir" ) ; args . push ( workDirUri . fsPath ) ; let configPath = workDirUri . toString ( ) + "/src/config.lua" ; let config = getQuickConfig ( configPath ) ; if ( config ) { let debug = config . debug ; if ( debug == 0 ) { args . push ( "-disable-write-debug-log" ) ; args . push ( "-disable-console" ) ; } else if ( debug == 1 ) { args . push ( "-disable-write-debug-log" ) ; args . push ( "-console" ) ; } else if ( debug == 2 ) { args . push ( "-write-debug-log" ) ; args . push ( "-console" ) ; } args . push ( "-size" ) ; args . push ( config . width + "x" + config . height ) ; } playerProcess = execFile ( playerFsPath , args ) ; } function doRunGameRunner ( workDirUri ) { let runnerPath = quickRoot + "/tools/runner/bin/" ; if ( process . platform == "win32" ) { runnerPath += "win32/LuaGameRunner.exe" ; } else if ( process . platform == "darwin" ) { runnerPath += "LuaGameRunner.app/Contents/MacOS/LuaGameRunner" ; } else { window . showErrorMessage ( "抱歉，不支持当前制作系统!" ) ; return ; } if ( ! existsSync ( new URL ( runnerPath ) ) ) { window . showErrorMessage ( "LuaGameRunner不存在" ) ; return ; } let runnerFsPath = URI . parse ( runnerPath ) . fsPath ; let args = [ ] ; args . push ( "--gamedir" ) ; args . push ( workDirUri . fsPath ) ; args . push ( "--writedir" ) ; args . push ( workDirUri . fsPath ) ; let configPath = workDirUri . toString ( ) + "/src/config.lua" ; let config = getQuickConfig ( configPath ) ; if ( config ) { let debug = config . debug ; if ( debug > 0 ) { args . push ( "--log" ) ; } args . push ( "--width" ) ; args . push ( config . width + "" ) ; args . push ( "--height" ) ; args . push ( config . height + "" ) ; } playerProcess = execFile ( runnerFsPath , args ) ; } function getQuickConfig ( configPath ) { if ( ! existsSync ( new URL ( configPath ) ) ) { return null ; } let re = { debug : 1 , width : 640 , height : 480 } ; let data = readFileSync ( new URL ( configPath ) , "utf-8" ) ; let lines = data . split ( "\n" ) ; for ( let i = 0 ; i < lines . length ; i ++ ) { const line = lines [ i ] ; if ( line . startsWith ( "--" ) ) { continue ; } let arr = line . split ( "=" ) ; if ( arr . length == 2 ) { let key = arr [ 0 ] . trim ( ) ; let value = arr [ 1 ] . trim ( ) ; if ( key == "DEBUG" ) { re . debug = parseInt ( value ) ; } else if ( key == "CONFIG_SCREEN_WIDTH" ) { re . width = parseInt ( value ) ; } else if ( key == "CONFIG_SCREEN_HEIGHT" ) { re . height = parseInt ( value ) ; } } } return re ; } export function activate ( context ) { serverModule = context . asAbsolutePath ( path . join ( "out" , "src" , "server" , "server.js" ) ) ; outputChannel = window . createOutputChannel ( "QuickXDev" ) ; workspace . onDidOpenTextDocument ( didOpenTextDocument ) ; workspace . textDocuments . forEach ( didOpenTextDocument ) ; workspace . onDidChangeWorkspaceFolders ( ( event ) => { for ( const folder of event . removed ) { let client = clients . get ( folder . uri . toString ( ) ) ; if ( client ) { clients . delete ( folder . uri . toString ( ) ) ; client . stop ( ) ; } } } ) ; let openPlayerCmd = commands . registerCommand ( "quickxdev.runPlayer" , runPlayer ) ; context . subscriptions . push ( openPlayerCmd ) ; } export function deactivate ( ) { let promises = [ ] ; for ( const client of clients . values ( ) ) { promises . push ( client . stop ( ) ) ; } return Promise . all ( promises ) . then ( ( ) => undefined ) ; }