From bf59abda078fd0e08504af54dd98a4972e8ebd3a Mon Sep 17 00:00:00 2001 From: Mike Nolan Date: Tue, 23 Dec 2025 16:54:07 -0600 Subject: [PATCH] Add more to plugin --- .../res/cppfiletemplate.cpp | 13 +- .../src/FilePublisher.tcross | 262 +++++++++ .../src/buildtool.tcross | 5 +- Tesses.CrossLang.Shell/src/build.tcross | 13 +- Tesses.CrossLang.Shell/src/debug.tcross | 1 + Tesses.CrossLang.Shell/src/run.tcross | 9 +- Tesses.CrossLang.Shell/src/webapp-test.tcross | 7 +- Tesses.CrossLang.Shell/src/webapp.tcross | 9 +- vscode-extension/eslint.config.mjs | 27 + vscode-extension/logo.png | Bin 0 -> 17371 bytes vscode-extension/media/add-package.js | 79 +++ vscode-extension/media/jsconfig.json | 19 + vscode-extension/media/reset.css | 30 + vscode-extension/media/vscode.css | 118 ++++ vscode-extension/package.json | 80 ++- vscode-extension/src/api.ts | 541 ++++++++++++++++++ vscode-extension/src/extension.ts | 31 + vscode-extension/src/test/extension.test.ts | 15 + .../syntaxes/crossasm.tmLanguage.json | 133 +++++ .../syntaxes/crosslang.tmLanguage.json | 4 +- vscode-extension/tsconfig.json | 17 + 21 files changed, 1394 insertions(+), 19 deletions(-) create mode 100644 Tesses.CrossLang.BuildEssentials/src/FilePublisher.tcross create mode 100644 vscode-extension/eslint.config.mjs create mode 100644 vscode-extension/logo.png create mode 100644 vscode-extension/media/add-package.js create mode 100644 vscode-extension/media/jsconfig.json create mode 100644 vscode-extension/media/reset.css create mode 100644 vscode-extension/media/vscode.css create mode 100644 vscode-extension/src/api.ts create mode 100644 vscode-extension/src/extension.ts create mode 100644 vscode-extension/src/test/extension.test.ts create mode 100644 vscode-extension/syntaxes/crossasm.tmLanguage.json create mode 100644 vscode-extension/tsconfig.json diff --git a/Tesses.CrossLang.BuildEssentials/res/cppfiletemplate.cpp b/Tesses.CrossLang.BuildEssentials/res/cppfiletemplate.cpp index 052e6c5..042302b 100644 --- a/Tesses.CrossLang.BuildEssentials/res/cppfiletemplate.cpp +++ b/Tesses.CrossLang.BuildEssentials/res/cppfiletemplate.cpp @@ -19,12 +19,12 @@ int main(int argc, char** argv) TStd::RegisterStd(&gc,env); { - auto ms = std::shared_ptr(false); + auto ms = std::make_shared(false); auto& buff = ms->GetBuffer(); buff.resize(@<>@_length); memcpy(buff.data(),@<>@_data,@<>@_length); auto file = TFile::Create(ls); - file->Load(&gc, &ms); + file->Load(&gc, ms); env->LoadFile(&gc,file); } @@ -41,6 +41,7 @@ int main(int argc, char** argv) port = std::stoi(item.second); } } + env->EnsureDictionary(&gc,"Net")->SetValue("WebServerPort", (int64_t)port); TList* args2 = TList::Create(ls); args2->Add(exePath.ToString()); for(auto& item : args.positional) @@ -48,9 +49,9 @@ int main(int argc, char** argv) args2->Add(item); } - auto res = env->CallFunction(ls, "WebAppMain", {args2}); - TObjectHttpServer http(&gc, res); - Tesses::Framework::Http::HttpServer svr(port,http,false); + auto res = env->CallFunctionWithFatalError(ls, "WebAppMain", {args2}); + auto http=std::make_shared(&gc, res); + Tesses::Framework::Http::HttpServer svr((uint16_t)port,http); svr.StartAccepting(); TF_RunEventLoop(); @@ -71,7 +72,7 @@ int main(int argc, char** argv) args->Add(exePath.ToString()); for(int arg=1;argAdd(argv[arg]); - auto res = env->CallFunction(ls,"main",{args}); + auto res = env->CallFunctionWithFatalError(ls,"main",{args}); int64_t iresult; if(GetObject(res,iresult)) return (int)iresult; diff --git a/Tesses.CrossLang.BuildEssentials/src/FilePublisher.tcross b/Tesses.CrossLang.BuildEssentials/src/FilePublisher.tcross new file mode 100644 index 0000000..91c0006 --- /dev/null +++ b/Tesses.CrossLang.BuildEssentials/src/FilePublisher.tcross @@ -0,0 +1,262 @@ +class FilePublisher { + public FilePublisher(program, publish) + { + this.Program = program; + this.OutputDirectory = publish; + } + + /^ + The file to publish + ^/ + public Program; + + /^ + The {name,version,info} from executable + ^/ + public ProjectInfo; + /^ + Where we output the published files + ^/ + public OutputDirectory; + /^ + The runtime identifier + + builtins: + "crvm" (merge to one crvm) + "header" (merge to one crvm and generate C header) + "cmake" (merge to one crvm and generate C header and create a cmake project) + + actual runtimes can be found here: https://cpkg.tesseslanguage.com/packages?q=Tesses.CrossLang.Runtime. (you need to strip the Tesses.CrossLang.Runtime. from the names) + + ^/ + public RuntimeIdentifier="crvm"; + /^ + The prefix of the package (to prepend onto runtime identifier, we put the trailing period for you) + ^/ + public PackagePrefix = "Tesses.CrossLang.Runtime"; + /^ + Merge for runtimes (one crvm file) + ^/ + public MergeForRuntimes = false; + + + private Build() + { + var strm = FS.Local.OpenFile(this.Program, "rb"); + var exec=VM.LoadExecutable(strm); + strm.Close(); + this.ProgramInfo = { + name = exec.Name, + version = exec.Version.ToString(), + info = Json.Decode(exec.Info) + }; + + return FS.MakeFull(this.Program); + } + + private Merge() + { + var path = this.Build(); + var packPath = path / "__merge"; + if(packPath.Count > 0 && FS.Local.DirectoryExists(packPath)) + { + FS.Local.DeleteDirectoryRecurse(packPath); + } + FS.Local.CreateDirectory(packPath); + + var pack_dir = new SubdirFilesystem(FS.Local, packPath); + + return packPath / VM.Merge(FS.Local,path,pack_dir); + } + private CopyBuild(destPath) + { + func CopyCRVM(src,dest) + { + func copyFile(src,dest) + { + var _src = FS.Local.OpenFile(src,"rb"); + var _dest = FS.Local.OpenFile(dest, "wb"); + _src.CopyTo(_dest); + _src.Close(); + _dest.Close(); + Console.WriteLine($"{src} -> {dest}"); + } + if(FS.Local.FileExists(dest)) return; + copyFile(src,dest); + + + var srcStrm = FS.Local.OpenFile(src,"rb"); + var crvm = VM.LoadExecutable(srcStrm); + + srcStrm.Close(); + each(var dep : crvm.Dependencies) + { + var name = $"{dep.Name}-{dep.Version.ToString()}.crvm"; + CopyCRVM(src.GetParent()/name, dest.GetParent()/name); + } + } + each(var item : FS.Local.EnumeratePaths(destPath.GetParent())) + { + if(item.GetExtension() == ".crvm" && FS.Local.FileExists(item)) + { + FS.Local.DeleteFile(item); + } + } + CopyCRVM(this.Build(), destPath); + } + + private CreateCMake(dir, short_name) + { + FS.Local.CreateDirectory(dir / "src"); + var path = this.Merge(); + var bytes = FS.ReadAllBytes(FS.Local, path); + var caps_version = this.FixCHeaderName(short_name); + + FS.WriteAllText(FS.Local, dir / "src" / $"{short_name}.h", bytes.ToCHeaderFile(caps_version)); + + if(!FS.Local.RegularFileExists(dir / "CMakeLists.txt")) + { + var cmake_file = embed("cml.txt").ToString().Replace("@<>@",caps_version).Replace("@<>@",short_name); + FS.WriteAllText(FS.Local,dir / "CMakeLists.txt", cmake_file); + } + + if(!FS.Local.RegularFileExists(dir / "src"/"main.cpp")) + { + + var cppFile = embed("cppfiletemplate.cpp").ToString().Replace("@<>@", caps_version).Replace("@<
>@",short_name); + + FS.WriteAllText(FS.Local,dir / "src"/"main.cpp", cppFile); + } + } + + private CopyFile(src,dest) + { + var srcStrm = FS.Local.OpenFile(src,"rb"); + var destStrm = FS.Local.OpenFile(dest, "wb"); + + srcStrm.CopyTo(destStrm); + srcStrm.Close(); + destStrm.Close(); + } + + private FixCHeaderName(name) + { + var myStr = name.ToUpper(); + name = ""; + each(var item : myStr) + { + if((item >= 'A' && item <= 'Z') || (item >= '0' && item <= '9') || item == '_') + name += item; + } + if(name.Length == 0) return "NONAME"; + else if(name[0] >= '0' && name[0] <= '9') + return $"_{name}"; + return name; + } + + + /^ Start the publishing process ^/ + public Publish() + { + + + var outDir =this.OutputDirectory; + var short_name = TypeOf(this.ProjectInfo.info.short_name) == "String" ? this.ProjectInfo.info.short_name : this.ProjectInfo.name; + + FS.Local.CreateDirectory(outDir); + + switch(this.RuntimeIdentifier) + { + case "copy": + { + if(this.MergeForRuntimes) + { + FS.Local.MoveFile(this.Merge(), outDir / short_name + ".crvm"); + } + else { + this.CopyBuild(outDir / short_name + ".crvm"); + } + } + break; + case "crvm": + { + var path = this.Merge(); + FS.Local.MoveFile(path, outDir / $"{short_name}.crvm"); + } + break; + case "header": + { + var path = this.Merge(); + var bytes = FS.ReadAllBytes(FS.Local, path); + FS.WriteAllText(FS.Local, outDir / $"{short_name}.h", bytes.ToCHeaderFile(this.FixCHeaderName(short_name))); + } + break; + case "cmake": + { + this.CreateCMake(outDir, short_name); + } + break; + default: + { + var runtimePackageName = this.PackagePrefix; + + if(runtimePackageName.Count > 0 && !runtimePackageName.EndsWith('.')) + runtimePackageName += "."; + + runtimePackageName += this.RuntimeIdentifier; + + var version = this.ProjectBuilder.PackageManager.GetLatest(runtimePackageName); + if(TypeOf(version) != "String") + { + throw { + Type="PackageNotFoundException", + Message=$"Could not find package {runtimePackageName}.", + ToString=(this)=>$"{this.Type} on line: {this.Line} {this.Message}" + }; + } + + var pkgObj = this.ProjectBuilder.PackageManager.GetPackage(runtimePackageName, version); + var strm = new MemoryStream(true); + strm.WriteBlock(pkgObj,0,pkgObj.Length); + strm.Seek(0,0); + var sdfs = new SubdirFilesystem(FS.Local, outDir); + var archiveResponse = FS.ExtractArchive(strm,sdfs); + sdfs.Close(); + + var archiveInfo = Json.Decode(archiveResponse.Info); + + if(archiveInfo.executable_can_be_renamed) + { + var executable_name_path = /archiveInfo.executable_name; + var executable_extension = executable_name_path.GetExtension(); + var newName = outDir/short_name; + if(executable_extension.Length > 0) newName += executable_extension; + + FS.Local.MoveFile(outDir/archiveInfo.executable_name,newName); + FS.Local.Chmod(newName, 0755); + if(this.MergeForRuntimes) + { + FS.Local.MoveFile(this.Merge(), outDir / short_name + ".crvm"); + } + else { + this.CopyBuild(outDir / short_name + ".crvm"); + } + } + else { + var executable_name_path = /archiveInfo.executable_name; + FS.Local.Chmod(outDir/archiveInfo.executable_name, 0755); + var executable_name_no_ext = executable_name_path.ChangeExtension(null).GetFileName(); + if(this.MergeForRuntimes) + { + FS.Local.MoveFile(this.Merge(), outDir / executable_name_no_ext + ".crvm"); + } + else { + this.CopyBuild(outDir / executable_name_no_ext + ".crvm"); + } + } + } + break; + + } + } +} \ No newline at end of file diff --git a/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross b/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross index 073a131..50e2c63 100644 --- a/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross +++ b/Tesses.CrossLang.BuildEssentials/src/buildtool.tcross @@ -15,6 +15,8 @@ class Tesses.CrossLang.BuildTool srcStrm.Close(); destStrm.Close(); } + /^ Whether debug is enabled ^/ + public Debug = false; /^ Package Manager Object ^/ public PackageManager; /^ Directories compiled ^/ @@ -355,7 +357,8 @@ class Tesses.CrossLang.BuildTool ResourceFileSystem = new SubdirFilesystem(FS.Local, dir / resDir), Dependencies = file_deps, Output = outFile, - CompTime = compTimeEnv + CompTime = compTimeEnv, + Debug = this.Debug }); outFile.Close(); diff --git a/Tesses.CrossLang.Shell/src/build.tcross b/Tesses.CrossLang.Shell/src/build.tcross index 2fd8750..657842e 100644 --- a/Tesses.CrossLang.Shell/src/build.tcross +++ b/Tesses.CrossLang.Shell/src/build.tcross @@ -3,27 +3,33 @@ func Tesses.CrossLang.Shell.Build(dd) var offline=false; var allowFullCompTime=false; var buildPath = "."; + var debug=false; if(dd.Arguments.Count > 1) { buildPath = dd.Arguments[1]; } each(var flag : dd.Flags) { - if(flag == "offline") + if(flag == "debug") + { + debug = true; + } + else if(flag == "offline") { offline = true; } - if(flag == "allow-insecure-comptime") + else if(flag == "allow-insecure-comptime") { allowFullCompTime=true; } - if(flag == "help") + else if(flag == "help") { Console.WriteLine("USAGE: crosslang build [build-flags-and-options]"); Console.WriteLine("FLAGS:"); Console.WriteLine("offline: build with no internet (don't fetch files)"); Console.WriteLine("help: this help"); Console.WriteLine("allow-insecure-comptime: Allow full comptime"); + Console.WriteLine("debug: emit line info into executable"); Console.WriteLine(); Console.WriteLine("OPTIONS:"); Console.WriteLine("conf=CONFIGSTRING: specify a conf string for compile_tool(s), is the property Config"); @@ -42,6 +48,7 @@ func Tesses.CrossLang.Shell.Build(dd) pm.Offline = offline; var bt = new Tesses.CrossLang.BuildTool(pm); bt.Config = conf; + bt.Debug = debug; bt.AllowFullCompTime = allowFullCompTime; bt.BuildProject(buildPath); diff --git a/Tesses.CrossLang.Shell/src/debug.tcross b/Tesses.CrossLang.Shell/src/debug.tcross index 769b2c6..2d2afeb 100644 --- a/Tesses.CrossLang.Shell/src/debug.tcross +++ b/Tesses.CrossLang.Shell/src/debug.tcross @@ -61,6 +61,7 @@ func Tesses.CrossLang.Shell.Debug(dd) var pm = new Tesses.CrossLang.PackageManager(); pm.Offline = offline; var bt = new Tesses.CrossLang.BuildTool(pm); + bt.Debug = true; bt.Config = conf; output = bt.BuildProject(buildPath).Output; } diff --git a/Tesses.CrossLang.Shell/src/run.tcross b/Tesses.CrossLang.Shell/src/run.tcross index 7a7b7dd..381c8ea 100644 --- a/Tesses.CrossLang.Shell/src/run.tcross +++ b/Tesses.CrossLang.Shell/src/run.tcross @@ -5,9 +5,14 @@ func Tesses.CrossLang.Shell.Run(dd) var nobuild=false; var allowFullCompTime=false; var output=""; + var debug = false; each(var flag : dd.Flags) { - if(flag == "offline") + if(flag == "debug") + { + debug = true; + } + else if(flag == "offline") { offline = true; } @@ -23,6 +28,7 @@ func Tesses.CrossLang.Shell.Run(dd) Console.WriteLine("offline: build with no internet (don't fetch files)"); Console.WriteLine("allow-insecure-comptime: Allow full comptime"); Console.WriteLine("nobuild: don't build, just run"); + Console.WriteLine("debug: emit line info into executable"); Console.WriteLine("help: this help"); Console.WriteLine(); Console.WriteLine("OPTIONS:"); @@ -68,6 +74,7 @@ func Tesses.CrossLang.Shell.Run(dd) pm.Offline = offline; var bt = new Tesses.CrossLang.BuildTool(pm); bt.Config = conf; + bt.Debug = debug; bt.AllowFullCompTime = allowFullCompTime; output = bt.BuildProject(buildPath).Output; } diff --git a/Tesses.CrossLang.Shell/src/webapp-test.tcross b/Tesses.CrossLang.Shell/src/webapp-test.tcross index 6176ce7..7ba1627 100644 --- a/Tesses.CrossLang.Shell/src/webapp-test.tcross +++ b/Tesses.CrossLang.Shell/src/webapp-test.tcross @@ -20,11 +20,14 @@ func Tesses.CrossLang.Shell.WebAppTest(dd) var proj=bt.BuildProject("."); var output = proj.Output; - - var env = VM.CreateEnvironment({}); + const _dict = {}; + var env = VM.CreateEnvironment(_dict); env.RegisterEverything(); env.LockRegister(); + _dict.Net ?? = {}; + _dict.Net.WebServerPort = port; + env.LoadFileWithDependencies(FS.Local,output); var myArgs = [output.ToString()]; for(var i = 1; i < dd.Arguments.Count; i++) diff --git a/Tesses.CrossLang.Shell/src/webapp.tcross b/Tesses.CrossLang.Shell/src/webapp.tcross index b2dccbb..ca2383a 100644 --- a/Tesses.CrossLang.Shell/src/webapp.tcross +++ b/Tesses.CrossLang.Shell/src/webapp.tcross @@ -27,13 +27,18 @@ func Tesses.CrossLang.Shell.WebApp(dd) } } + if(FS.Local.FileExists(webapppath)) { - - var env = VM.CreateEnvironment({}); + const _dict = {}; + const env = VM.CreateEnvironment(_dict); env.RegisterEverything(); env.LockRegister(); + _dict.Net ?? = {}; + _dict.Net.WebServerPort = port; + + env.LoadFileWithDependencies(FS.Local,webapppath); var myArgs = [webapppath.ToString()]; for(var i = 2; i < dd.Arguments.Count; i++) diff --git a/vscode-extension/eslint.config.mjs b/vscode-extension/eslint.config.mjs new file mode 100644 index 0000000..7c51b0c --- /dev/null +++ b/vscode-extension/eslint.config.mjs @@ -0,0 +1,27 @@ +import typescriptEslint from "typescript-eslint"; + +export default [{ + files: ["**/*.ts"], +}, { + plugins: { + "@typescript-eslint": typescriptEslint.plugin, + }, + + languageOptions: { + parser: typescriptEslint.parser, + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + "@typescript-eslint/naming-convention": ["warn", { + selector: "import", + format: ["camelCase", "PascalCase"], + }], + + curly: "warn", + eqeqeq: "warn", + "no-throw-literal": "warn", + semi: "warn", + }, +}]; \ No newline at end of file diff --git a/vscode-extension/logo.png b/vscode-extension/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..bfb532e49dd3282fe770862a078e71c51a89c3e6 GIT binary patch literal 17371 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 + page = 1; + currentPage.innerText = "1"; + vscode.postMessage( + {command: 'query', query: query.value, page: 1}, + ); + }; + if(prevPage) + prevPage.onclick = ()=>{ + page--; + if(page < 1) page=1; + currentPage.innerText = page.toString(); + vscode.postMessage( + {command: 'query', query: query.value, page: page}, + ); + }; + if(nextPage) + nextPage.onclick = ()=>{ + page++; + currentPage.innerText = page.toString(); + vscode.postMessage( + {command: 'query', query: query.value, page: page}, + ); + }; + + + window.addEventListener('message', event => { + const message = event.data; // The json data that the extension sent + switch (message.command) { + case "searchResponse": + { + body.innerHTML = ""; + + for(var i = 0; i < message.packages.length; i++) + { + const pkg = message.packages[i]; + const node = document.createElement('div'); + node.classList.add('package'); + const img = document.createElement('img'); + img.src = pkg.icon; + img.width = 64; + img.height = 64; + node.appendChild(img); + const title = document.createElement('span'); + title.innerText = pkg.name; + node.appendChild(title); + const btn = document.createElement('button'); + btn.innerText = pkg.installText; + btn.onclick = ()=>{ + vscode.postMessage({ + command: "manage-pkg", + name: pkg.name, + version: pkg.version, + query: message.query, + page: message.page + }); + }; + node.appendChild(btn); + + body.appendChild(node); + } + } + break; + } + }); +}()); \ No newline at end of file diff --git a/vscode-extension/media/jsconfig.json b/vscode-extension/media/jsconfig.json new file mode 100644 index 0000000..60eb011 --- /dev/null +++ b/vscode-extension/media/jsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2020", + "jsx": "preserve", + "lib": [ + "dom" + ] + }, + "exclude": [ + "node_modules", + "**/node_modules/*" + ], + "typeAcquisition": { + "include": [ + "@types/vscode-webview" + ] + } +} \ No newline at end of file diff --git a/vscode-extension/media/reset.css b/vscode-extension/media/reset.css new file mode 100644 index 0000000..92d0291 --- /dev/null +++ b/vscode-extension/media/reset.css @@ -0,0 +1,30 @@ +html { + box-sizing: border-box; + font-size: 13px; +} + +*, +*:before, +*:after { + box-sizing: inherit; +} + +body, +h1, +h2, +h3, +h4, +h5, +h6, +p, +ol, +ul { + margin: 0; + padding: 0; + font-weight: normal; +} + +img { + max-width: 100%; + height: auto; +} diff --git a/vscode-extension/media/vscode.css b/vscode-extension/media/vscode.css new file mode 100644 index 0000000..cbb1b35 --- /dev/null +++ b/vscode-extension/media/vscode.css @@ -0,0 +1,118 @@ +:root { + --container-padding: 20px; + --input-padding-vertical: 6px; + --input-padding-horizontal: 4px; + --input-margin-vertical: 4px; + --input-margin-horizontal: 0; +} + +body { + padding: 0 var(--container-padding); + color: var(--vscode-foreground); + font-size: var(--vscode-font-size); + font-weight: var(--vscode-font-weight); + font-family: var(--vscode-font-family); + background-color: var(--vscode-editor-background); +} + +ol, +ul { + padding-left: var(--container-padding); +} + +body > *, +form > * { + margin-block-start: var(--input-margin-vertical); + margin-block-end: var(--input-margin-vertical); +} + +*:focus { + outline-color: var(--vscode-focusBorder) !important; +} + +a { + color: var(--vscode-textLink-foreground); +} + +a:hover, +a:active { + color: var(--vscode-textLink-activeForeground); +} + +code { + font-size: var(--vscode-editor-font-size); + font-family: var(--vscode-editor-font-family); +} + +button { + border: none; + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + width: 100%; + text-align: center; + outline: 1px solid transparent; + outline-offset: 2px !important; + color: var(--vscode-button-foreground); + background: var(--vscode-button-background); +} + +button:hover { + cursor: pointer; + background: var(--vscode-button-hoverBackground); +} + +button:focus { + outline-color: var(--vscode-focusBorder); +} + +button.secondary { + color: var(--vscode-button-secondaryForeground); + background: var(--vscode-button-secondaryBackground); +} + +button.secondary:hover { + background: var(--vscode-button-secondaryHoverBackground); +} + +input:not([type='checkbox']), +textarea { + display: block; + width: 100%; + border: none; + font-family: var(--vscode-font-family); + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + color: var(--vscode-input-foreground); + outline-color: var(--vscode-input-border); + background-color: var(--vscode-input-background); +} + +input::placeholder, +textarea::placeholder { + color: var(--vscode-input-placeholderForeground); +} + + +.search-box { + display: grid; + grid-template-columns: minmax(100px,auto) 100px; + column-gap: 1rem; +} + +.package { + display: grid; + grid-template-columns: 64px minmax(100px,auto) 100px; + column-gap: 1rem; +} +.package button { + height: fit-content; +} + +.pagination { + + display: grid; + grid-template-columns: auto 100px 50px 100px auto; + column-gap: 1rem; +} +.pagination span { + width: 100%; + text-align: center; +} \ No newline at end of file diff --git a/vscode-extension/package.json b/vscode-extension/package.json index e7b5cab..d0e8118 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -17,11 +17,87 @@ "aliases": ["CrossLang", "crosslang"], "extensions": [".tcross"], "configuration": "./language-configuration.json" + },{ + "id": "crossasm", + "aliases": ["CrossAsm", "crossasm"], + "extensions": [".tcasm"], + "configuration": "./language-configuration.json" }], "grammars": [{ "language": "crosslang", "scopeName": "source.crosslang", "path": "./syntaxes/crosslang.tmLanguage.json" - }] - } + },{ + "language": "crossasm", + "scopeName": "source.crossasm", + "path": "./syntaxes/crossasm.tmLanguage.json" + }], + "commands": [ + { + "command": "crosslang.createProject", + "title": "Create Project", + "category": "CrossLang" + }, + { + "command": "crosslang.createProjectWorkspace", + "title": "Create Project in workspace", + "category": "CrossLang" + }, + { + "command": "crosslang.uploadPackage", + "title": "Upload package to CPKG", + "category": "CrossLang" + }, + { + "command": "crosslang.addPackage", + "title": "Add Package", + "category": "CrossLang" + } + ], + "debuggers": [ + { + "type": "crosslang", + "languages": [ + "crosslang" + ], + "configurationSnippets": [ + { + "label": "CrossLang: Launch", + "description": "A new configuration for CrossLang", + "body": { + "name": "Run crosslang program", + "type": "node-terminal", + "request": "launch", + "command": "crosslang run", + "cwd": "^\"\\${workspaceFolder}/\"" + } + } + ] + } + ] + }, + "activationEvents": [], + "main": "./out/extension.js", + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint src", + "test": "vscode-test" + }, + "devDependencies": { + "@types/mocha": "^10.0.10", + "@types/node": "22.x", + "@types/vscode": "^1.107.0", + "@vscode/test-cli": "^0.0.12", + "@vscode/test-electron": "^2.5.2", + "eslint": "^9.39.1", + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.3" + }, + "dependencies": { + "platform-folders": "^0.6.1" + }, + "icon": "logo.png" } diff --git a/vscode-extension/src/api.ts b/vscode-extension/src/api.ts new file mode 100644 index 0000000..03e91f7 --- /dev/null +++ b/vscode-extension/src/api.ts @@ -0,0 +1,541 @@ +import { spawn } from "node:child_process"; +import { QuickPickItem } from "vscode"; +import * as vscode from 'vscode'; +import { getDocumentsFolder } from 'platform-folders'; +import { join } from "node:path"; +import { existsSync, mkdirSync } from "node:fs"; +import { readFile, writeFile } from "node:fs/promises"; + +export interface CrossLangProjectConfigInfo +{ + type: string + maintainer?: string + repo?: string + homepage?: string + license?: string + plugin_host?: string + description?: string + executable_name?: string + executable_can_be_renamed?: boolean + executable_runtime?: string + template_ignored_files?: string[] + template_info?: CrossLangProjectConfigInfo +} +export interface CrossLangProjectDependency { + name: string + version: string +} +export interface CrossLangProjectConfig { + name: string + version: string + info: CrossLangProjectConfigInfo + resource_directory?: string + source_directory?: string + bin_directory?: string + obj_directory?: string + project_dependencies?: string[] + dependencies?: CrossLangProjectDependency[] +} + +export interface CrossLangTemplate { + name: string + description: string +} +export interface PackageServerResponseItem { + accountName: string + description: string + homepage: string + license: string + maintainer: string + packageName: string + repo: string + type: string + uploadDate: string + uploadTime: number + version: string +} +export interface PackageServerResponse { + packages: PackageServerResponseItem[] +} + + +export function readCommandToEnd(name: string, args: Array, working?:string) : Promise +{ + return new Promise((resolve,reject)=>{ + var str: string = ""; + const program = spawn(name, args, {cwd: working}); + program.stdout.on('data',(data)=>{ + str += `${data}`; + }); + program.on('close',(signal)=>{ + if(signal && signal != 0) + reject("Didn't exit successfully"); + resolve(str); + }) + }); +} +export async function ensureWorkingShell() +{ + if(!await hasShell()) + { + const install = await vscode.window.showInformationMessage('CrossLang shell is not installed, install?',"Yes","No"); + if(install === "Yes") + { + await updateShell(); + } + } +} + +export async function getCrossLangConfigDir() : Promise +{ + return (await readCommandToEnd('crosslang',['configdir'])).replace('\r','').replace('\n',''); +} + +export async function hasShell() : Promise +{ + try { + return existsSync(join(await getCrossLangConfigDir(),"Shell/Shell.crvm")); + } catch(ex) {return false;} +} + +export async function updateShell() +{ + await readCommandToEnd('crosslang',['update-shell']); +} + +export async function getWorkspace() : Promise +{ + const wsf = vscode.workspace.workspaceFolders; + if(wsf) + { + if(wsf.length === 1) return wsf[0].uri.fsPath ?? null; + if(wsf.length > 1) + { + return await new Promise((resolve,reject)=>{ + const qp=vscode.window.createQuickPick(); + qp.title = "Which workspace folder?"; + const qpItems :QuickPickItem[] = []; + for(var i = 0; i < wsf.length; i++) + { + + qpItems.push({ + label: wsf[i].name, + description: wsf[i].uri.fsPath + }); + } + qp.items = qpItems; + qp.onDidAccept(()=>{ + if(qp.activeItems.length ==1) + { + + qp.hide(); + resolve(qp.activeItems[0].description ?? null); + + } + else { + + qp.hide(); + resolve(null); + + } + }); + qp.show(); + }); + } + } + return null; +} + + +export async function getTemplatesAsync() : Promise +{ + const text = await readCommandToEnd('crosslang',['new','--list-json']); + return JSON.parse(text); +} +async function pickTemplate() : Promise +{ + const templates=await getTemplatesAsync(); + return await new Promise((resolve,reject)=>{ + const qp=vscode.window.createQuickPick(); + qp.title = "Select the template"; + const qpItems :QuickPickItem[] = []; + for(var i = 0; i < templates.length; i++) + qpItems.push({label: templates[i].name, description: templates[i].description}); + + qp.items = qpItems; + + qp.onDidAccept(()=>{ + if(qp.activeItems.length ==1) + { + qp.hide(); + resolve(qp.activeItems[0].label); + + } + else { + + qp.hide(); + } + }); + qp.show(); + + }); +} +export async function uploadPackage() +{ + const projectDir = await getWorkspace(); + if(projectDir) + { + try { + await readCommandToEnd('crosslang',['upload-package'],projectDir); + await vscode.window.showInformationMessage('Uploaded package successfully'); + } catch(ex) { + await vscode.window.showErrorMessage('Failed to upload to CPKG'); + } + } +} + +export async function createTemplate() +{ + const templateName = await pickTemplate(); + + const project_name = await vscode.window.showInputBox({title: "Project name"}); + if(project_name) + { + const dir = join(getDocumentsFolder(),"CrossLangProjects",project_name); + mkdirSync(dir); + + await readCommandToEnd("crosslang",["new",templateName,dir]); + + let uri = vscode.Uri.file(dir); + await vscode.commands.executeCommand('vscode.openFolder', uri, true) + } +} +export async function createTemplateInWorkspace() +{ + const templateName = await pickTemplate(); + const workspace = await getWorkspace(); + console.log(workspace); + if(workspace) + { + await readCommandToEnd("crosslang",["new",templateName,workspace]); + } +} +export async function getServerUrl() : Promise { + try { + const confDir = await getCrossLangConfigDir(); + const pkgServersFile = join(confDir,"package_servers.json"); + if(existsSync(pkgServersFile)) + { + const data = await readFile(pkgServersFile); + const array: string[] = JSON.parse(data.toString()); + + if(array.length === 0) return null; + if(array.length === 1) return array[0]; + return await new Promise((resolve,reject)=>{ + const qp=vscode.window.createQuickPick(); + qp.title = "Select the server"; + const qpItems :QuickPickItem[] = []; + for(var i = 0; i < array.length; i++) + qpItems.push({label: array[i]}); + + qp.items = qpItems; + + qp.onDidAccept(()=>{ + if(qp.activeItems.length ==1) + { + qp.hide(); + resolve(qp.activeItems[0].label); + + } + else { + + qp.hide(); + resolve(null); + } + }); + qp.show(); + + }); + + } + }catch(ex) { + return null; + } + + return "https://cpkg.tesseslanguage.com/"; +} +export function trimEndChar(str: string, trim: string): string +{ + if(trim.length!==1) throw new Error("trim must be one char"); + var i = str.length-1; + for(;i>=0;i--) + { + if(str[i] !== trim[0]) break; + } + return str.substring(0,i+1); +} +export async function addPackagePage(extensionUri: vscode.Uri) +{ + //package_servers.json + const projectDir =await getWorkspace(); + if(projectDir) + { + if(existsSync(join(projectDir,"cross.json"))) + { + await addPackagePageInternal(extensionUri,projectDir); + } + else { + await vscode.window.showErrorMessage('Workspace does not have cross.json'); + } + } + else { + await vscode.window.showErrorMessage('No workspace selected'); + } +} +export async function getProjectInfo(projectDir: string): Promise +{ + const file = join(projectDir,"cross.json"); + if(existsSync(file)) + { + const bytes = await readFile(file); + return JSON.parse(bytes.toString()); + } + return null; +} +export async function setProjectInfo(projectDir: string, config: CrossLangProjectConfig) +{ + const file = join(projectDir,"cross.json"); + await writeFile(file,JSON.stringify(config,null,4)); +} +export function version2number(version: string) : number +{ + const parts = version.split('-'); + if(parts.length === 2) + { + const dotPart = parts[0].split('.'); + if(dotPart.length === 4) + { + var number = 0; + switch(parts[1]) + { + case 'alpha': + number = 1; + break; + case 'beta': + number = 2; + break; + case 'prod': + number = 3; + break; + } + number += (parseInt(parts[3],10) % 65536) * 4; + number += (parseInt(parts[2],10) % 256) * 65536; + number += (parseInt(parts[1],10) % 256) * 16777216; + number += (parseInt(parts[0],10) % 256) * 4294967296; + return number; + } + } + return -1; +} +async function addPackagePageInternal(extensionUri: vscode.Uri, projectDir: string) : Promise +{ + + const server = await getServerUrl(); + const scriptPathOnDisk = vscode.Uri.joinPath(extensionUri, 'media', 'add-package.js'); + + // And the uri we use to load this script in the webview + + var panel = vscode.window.createWebviewPanel( + 'toolbox', + 'CrossLang Package Manager', + vscode.ViewColumn.One + ); + panel.webview.options = { + enableScripts: true + }; + + panel.webview.onDidReceiveMessage( + async message => { + console.log(message.command); + switch (message.command) { + case 'manage-pkg': + { + //console.log(`${message.name} ${message.version}`); + + const info = await getProjectInfo(projectDir); + var install=true; + if(info) + { + if(info.dependencies) + for(var j = 0; j < info.dependencies.length; j++) + { + if(message.name === info.dependencies[j].name) + { + + install=false; + if(version2number(message.version) > version2number(info.dependencies[j].version)) + { + //upgrade or remove + const result= await new Promise((resolve,reject)=>{ + const qp=vscode.window.createQuickPick(); + qp.title = "Update or remove package"; + + + qp.items = [ + {label: "Update",description: `Update ${message.name} to ${message.version}`}, + {label: "Remove",description: `Remove ${message.name}`} + ]; + + qp.onDidAccept(()=>{ + if(qp.activeItems.length ==1) + { + qp.hide(); + resolve(qp.activeItems[0].label === "Update"); + + } + else { + + qp.hide(); + resolve(null); + } + }); + qp.show(); + + }); + if(result !== null) + { + if(result) + { + info.dependencies[j].version = message.version; + break; + } + else + { + info.dependencies.splice(j,1); + break; + } + } + } + else + { + //remove + info.dependencies.splice(j,1); + break; + } + } + } + if(install) + (info.dependencies ??= []).push({name: message.name, version: message.version}); + + await setProjectInfo(projectDir,info); + + + } + + + } + case 'query': + { + const info = await getProjectInfo(projectDir); + + if(server && info) + { + const url = `${trimEndChar(server,'/')}/api/v1/search?q=${encodeURIComponent(message.query)}&type=lib,compile_tool&offset=${encodeURIComponent(message.page-1)}`; + console.log(url); + const response = await fetch(url); + if(response.status === 200) + { + const packageServerResponse : PackageServerResponse = await response.json() as PackageServerResponse; + const packages = []; + + + for(var i = 0; i < packageServerResponse.packages.length;i++) + { + var installText = "Install"; + const pkg = packageServerResponse.packages[i]; + if(info.dependencies) + for(var j = 0; j < info.dependencies.length; j++) + { + if(pkg.packageName === info.dependencies[j].name) + { + installText = (version2number(pkg.version) > version2number(info.dependencies[j].version)) ? "Upgrade/Remove" : "Remove"; + } + } + + packages.push({ + name: pkg.packageName, + version: pkg.version, + icon: `${trimEndChar(server,'/')}/api/v1/package_icon.png?name=${encodeURIComponent(pkg.packageName)}&version=${pkg.version}`, + installText: installText + }); + + + } + + panel.webview.postMessage({ + command: "searchResponse", + packages: packages, + page: message.page, + query: message.query + }); + } + } + } + break; + } + }, + null + ); + const scriptUri = panel.webview.asWebviewUri(scriptPathOnDisk); + // Local path to css styles + const styleResetPath = vscode.Uri.joinPath(extensionUri, 'media', 'reset.css'); + const stylesPathMainPath = vscode.Uri.joinPath(extensionUri, 'media', 'vscode.css'); + + // Uri to load styles into webview + const stylesResetUri = panel.webview.asWebviewUri(styleResetPath); + const stylesMainUri = panel.webview.asWebviewUri(stylesPathMainPath); + const nonce = getNonce(); + panel.webview.html = ` + + + + + + + + + + + + + + +
+ +
+ + + +`; +} + +function getNonce() : string { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} \ No newline at end of file diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts new file mode 100644 index 0000000..78868db --- /dev/null +++ b/vscode-extension/src/extension.ts @@ -0,0 +1,31 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { addPackagePage, createTemplate, createTemplateInWorkspace, ensureWorkingShell, uploadPackage } from './api'; +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + ensureWorkingShell(); + const createProjectCommand = vscode.commands.registerCommand('crosslang.createProject', async() => { + await createTemplate(); + }); + const createProjectWorkspaceCommand = vscode.commands.registerCommand('crosslang.createProjectWorkspace', async()=>{ + await createTemplateInWorkspace(); + }); + const uploadPackageCommand = vscode.commands.registerCommand('crosslang.uploadPackage', async()=>{ + await uploadPackage(); + }); + const addPackageCommand = vscode.commands.registerCommand('crosslang.addPackage', async()=>{ + await addPackagePage(context.extensionUri); + }); + + context.subscriptions.push(createProjectCommand); + context.subscriptions.push(createProjectWorkspaceCommand); + context.subscriptions.push(uploadPackageCommand); + context.subscriptions.push(addPackageCommand); + + +} + +// This method is called when your extension is deactivated +export function deactivate() {} diff --git a/vscode-extension/src/test/extension.test.ts b/vscode-extension/src/test/extension.test.ts new file mode 100644 index 0000000..4ca0ab4 --- /dev/null +++ b/vscode-extension/src/test/extension.test.ts @@ -0,0 +1,15 @@ +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +// import * as myExtension from '../../extension'; + +suite('Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/vscode-extension/syntaxes/crossasm.tmLanguage.json b/vscode-extension/syntaxes/crossasm.tmLanguage.json new file mode 100644 index 0000000..32cc274 --- /dev/null +++ b/vscode-extension/syntaxes/crossasm.tmLanguage.json @@ -0,0 +1,133 @@ +{ + "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json", + "name": "CrossAsm", + "patterns": [ + { + "include": "#comment" + }, + { + "include": "#keywords" + }, + { + "include": "#chars" + }, + { + "include": "#operators" + }, + { + "include": "#function" + }, + { + "include": "#identifier" + }, + { + "include": "#consts" + }, + { + "include": "#double" + }, + { + "include": "#long" + }, + { + "include": "#strings" + } + ], + "repository": { + "comment": { + "patterns": [{ + "name": "comment.line.double-slash.crossasm", + "begin": "//", + "end": "\\n" + }, + { + "name": "comment.block.crossasm", + "begin": "\\/\\*", + "end": "\\*\\/" + }, + { + "name": "comment.line.crossasm", + "begin": "#", + "end": "\\n" + }, + { + "name":"comment.block.documentation.crossasm", + "begin": "\\/\\^", + "end": "\\^\\/" + } + ] + }, + "strings": { + "name": "string.quoted.double.crossasm", + "begin": "\\\"", + "end": "\\\"", + "patterns": [ + { + "name": "constant.character.escape.crossasm", + "match": "\\\\." + } + ] + }, + "operators": { + "patterns": [{ + "name": "keyword.operator", + "match": "(\\+|\\-|\\*|\\\/|\\%|\\!|\\||\\&|\\^|\\<\\<|\\>\\>|\\<|\\>|\\=)" + }] + }, + "function": { + "patterns": [{ + "name": "entity.name.function.crossasm", + "match": "\\b[_a-zA-Z\\x80-\\xFF\\$][_a-zA-Z0-9\\x80-\\xFF\\$]*\\(" + }] + }, + "identifier": { + "patterns": [{ + "name": "entity.name.crossasm", + "match": "\\b[_a-zA-Z\\x80-\\xFF\\$][_a-zA-Z0-9\\x80-\\xFF\\$]*" + }] + }, + "double": { + "name": "constant.numeric.crossasm", + "match": "\\b[0-9][0-9]*\\.[0-9]*\\b" + }, + + "long": { + "patterns": [ + { + "name": "constant.numeric.crossasm", + "match": "\\b[0-9][0-9]*\\b" + } + ] + }, + "keywords": { + "patterns": [ + { + + "name": "constant.language.crossasm", + "match": "\\b(true|false|null|undefined|this)\\b" + }, + { + "name": "keyword.control.crossasm", + "match": "\\b(add|sub|mul|div|mod|lsh|rsh|bor|band|bnot|lt|gt|lte|gte|eq|neq|not|neg|xor|pop|dup|nop|lineinfo|embeddir|pushclosure|createdict|createarray|appendlist|appenddict|embed|embedstrm|push|scopebegin|scopeend|scopeendtimes|setvariable|getvariable|declarevariable|declareconstvariable|setfield|getfield|callfunction|callmethod|ret|jmpc|jmp|jmpundefined|defer|trycatch|throw|pushscopelessclosure|yield|breakpoint|jmpifbreak|jmpifcontinue|jmpifdefined|lbl)\\b" + }, + { + "name": "keyword.crossasm", + "match": "\\b(root|func|class|public|private|protected|static)\\b" + } + ] + }, + "chars": { + "name": "constant.character.crossasm", + "begin": "'", + "end": "'", + "patterns": [ + { + "name": "constant.character.escape.crossasm", + "match": "\\\\." + } + ] + } + }, + "scopeName": "source.crossasm" +} + diff --git a/vscode-extension/syntaxes/crosslang.tmLanguage.json b/vscode-extension/syntaxes/crosslang.tmLanguage.json index 3e3d5ce..837c6a0 100644 --- a/vscode-extension/syntaxes/crosslang.tmLanguage.json +++ b/vscode-extension/syntaxes/crosslang.tmLanguage.json @@ -112,11 +112,11 @@ }, { "name": "keyword.control.crosslang", - "match": "\\b(if|else|while|for|do|return|each|break|try|catch|finally|defer|enumerable|yield|switch|case|default|await)\\b" + "match": "\\b(if|else|while|for|do|return|each|break|try|catch|finally|defer|enumerable|yield|switch|case|default|await|breakpoint|throw)\\b" }, { "name": "keyword.crosslang", - "match": "\\b(var|const|func|class|public|private|protected|static|operator|embed|async)\\b" + "match": "\\b(var|const|func|class|public|private|protected|static|operator|embed|embeddir|embedstrm|comptime|async)\\b" } ] }, diff --git a/vscode-extension/tsconfig.json b/vscode-extension/tsconfig.json new file mode 100644 index 0000000..356580f --- /dev/null +++ b/vscode-extension/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "outDir": "out", + "lib": [ + "ES2022" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true, /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +}