diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000..af967595 --- /dev/null +++ b/.clang-format @@ -0,0 +1,215 @@ +--- +Language: Java +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: true +AlignConsecutiveBitFields: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveDeclarations: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignConsecutiveMacros: + Enabled: false + AcrossEmptyLines: false + AcrossComments: false + AlignCompound: false + PadOperators: false +AlignEscapedNewlines: Right +AlignOperands: Align +AlignTrailingComments: true +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortEnumsOnASingleLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortLambdasOnASingleLine: All +AllowShortIfStatementsOnASingleLine: AllIfsAndElse +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: MultiLine +AttributeMacros: + - __capability +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeConceptDeclarations: Always +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +QualifierAlignment: Leave +CompactNamespaces: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +EmptyLineAfterAccessModifier: Never +EmptyLineBeforeAccessModifier: LogicalBlock +ExperimentalAutoDetectBinPacking: false +PackConstructorInitializers: BinPack +BasedOnStyle: '' +ConstructorInitializerAllOnOneLineOrOnePerLine: false +AllowAllConstructorInitializersOnNextLine: true +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IfMacros: + - KJ_IF_MAYBE +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^"(llvm|llvm-c|clang|clang-c)/' + Priority: 2 + SortPriority: 0 + CaseSensitive: false + - Regex: '^(<|"(gtest|gmock|isl|json)/)' + Priority: 3 + SortPriority: 0 + CaseSensitive: false + - Regex: '.*' + Priority: 1 + SortPriority: 0 + CaseSensitive: false +IncludeIsMainRegex: '(Test)?$' +IncludeIsMainSourceRegex: '' +IndentAccessModifiers: false +IndentCaseLabels: false +IndentCaseBlocks: false +IndentGotoLabels: true +IndentPPDirectives: None +IndentExternBlock: AfterExternBlock +IndentRequiresClause: true +IndentWidth: 2 +IndentWrappedFunctionNames: false +InsertBraces: false +InsertTrailingCommas: None +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: true +LambdaBodyIndentation: Signature +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 2 +ObjCBreakBeforeNestedBlockParam: true +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 19 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakOpenParenthesis: 0 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 60 +PenaltyIndentedWhitespace: 0 +PointerAlignment: Right +PPIndentWidth: -1 +ReferenceAlignment: Pointer +ReflowComments: true +RemoveBracesLLVM: false +RequiresClausePosition: OwnLine +SeparateDefinitionBlocks: Leave +ShortNamespaceLines: 1 +SortIncludes: Never +SortJavaStaticImport: Before +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeParensOptions: + AfterControlStatements: true + AfterForeachMacros: true + AfterFunctionDefinitionName: false + AfterFunctionDeclarationName: false + AfterIfMacros: true + AfterOverloadedOperator: false + AfterRequiresInClause: false + AfterRequiresInExpression: false + BeforeNonEmptyParentheses: false +SpaceAroundPointerQualifiers: Default +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInConditionalStatement: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInLineCommentPrefix: + Minimum: 1 + Maximum: -1 +SpacesInParentheses: false +SpacesInSquareBrackets: false +SpaceBeforeSquareBrackets: false +BitFieldColonSpacing: Both +Standard: Latest +StatementAttributeLikeMacros: + - Q_EMIT +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 2 +UseCRLF: false +UseTab: Always +WhitespaceSensitiveMacros: + - STRINGIZE + - PP_STRINGIZE + - BOOST_PP_STRINGIZE + - NS_SWIFT_NAME + - CF_SWIFT_NAME +... + diff --git a/.project b/.project index bb736408..aa8a35f8 100644 --- a/.project +++ b/.project @@ -31,4 +31,15 @@ org.eclipse.pde.PluginNature org.eclipse.pde.api.tools.apiAnalysisNature + + + 1709682485116 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + diff --git a/Makefile b/Makefile index 4b0e7e8e..b55a86e7 100644 --- a/Makefile +++ b/Makefile @@ -9,14 +9,19 @@ VERSION=`head -1 ChangeLog.txt | sed 's/^SASyLF version \(.*\).$$/\1/'`.v`date + default: test +build : SHELL:=/bin/bash build : (cd src && cd edu && cd cmu && cd cs && cd sasylf && cd parser; javacc parser.jj) mkdir -p bin - (cd src && javac -source 1.8 -target 1.8 -classpath ../bin:. -d ../bin edu/cmu/cs/sasylf/Main.java edu/cmu/cs/sasylf/term/UnitTests.java) + (cd src && javac -cp .:../bin:../lib/* -source 1.8 -target 1.8 -d ../bin edu/cmu/cs/sasylf/Main.java edu/cmu/cs/sasylf/VSCodeExtension.java ${TESTSRC}) jar cmf sasylf.mf SASyLF.jar ChangeLog.txt -C bin edu -C library org TESTBIN= bin/org/sasylf/Activator.class TESTLIB= bin/org/sasylf/util/Natural.slf +TESTSRC= edu/cmu/cs/sasylf/term/UnitTests.java \ + edu/cmu/cs/sasylf/util/UnitTests.java \ + edu/cmu/cs/sasylf/reduction/UnitTests.java + build-plugin : ${TESTBIN} ${TESTLIB} ChangeLog.txt jar cmf META-INF/MANIFEST.MF org.sasylf_${VERSION}.jar plugin.xml ChangeLog.txt icons/*.gif icons/*.png -C bin . @@ -29,6 +34,7 @@ ${TESTBIN}: ${TESTLIB}: ${MAKE} install-lib + .PHONY: install-lib install-lib: cp -r library/org bin/. diff --git a/lib/jackson-annotations-2.14.2.jar b/lib/jackson-annotations-2.14.2.jar new file mode 100644 index 00000000..7f52c92f Binary files /dev/null and b/lib/jackson-annotations-2.14.2.jar differ diff --git a/lib/jackson-core-2.14.2.jar b/lib/jackson-core-2.14.2.jar new file mode 100644 index 00000000..53c1b16a Binary files /dev/null and b/lib/jackson-core-2.14.2.jar differ diff --git a/lib/jackson-databind-2.14.2.jar b/lib/jackson-databind-2.14.2.jar new file mode 100644 index 00000000..a1cebc30 Binary files /dev/null and b/lib/jackson-databind-2.14.2.jar differ diff --git a/lsp-sasylf/.eslintignore b/lsp-sasylf/.eslintignore new file mode 100644 index 00000000..1d390969 --- /dev/null +++ b/lsp-sasylf/.eslintignore @@ -0,0 +1,5 @@ +node_modules/** +client/node_modules/** +client/out/** +server/node_modules/** +server/out/** \ No newline at end of file diff --git a/lsp-sasylf/.eslintrc.js b/lsp-sasylf/.eslintrc.js new file mode 100644 index 00000000..f660e395 --- /dev/null +++ b/lsp-sasylf/.eslintrc.js @@ -0,0 +1,20 @@ +/**@type {import('eslint').Linter.Config} */ +// eslint-disable-next-line no-undef +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended', + ], + rules: { + 'semi': [2, "always"], + '@typescript-eslint/no-unused-vars': 0, + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/explicit-module-boundary-types': 0, + '@typescript-eslint/no-non-null-assertion': 0, + } +}; \ No newline at end of file diff --git a/lsp-sasylf/.gitignore b/lsp-sasylf/.gitignore new file mode 100644 index 00000000..f59ad132 --- /dev/null +++ b/lsp-sasylf/.gitignore @@ -0,0 +1,10 @@ +out +node_modules +client/server +.vscode-test +package-lock.json +app/widgets/__pycache__/ +server/SASyLF.jar +electron_app/SASyLF.jar +server/lib/*.jar +electron_app/lib/*.jar diff --git a/lsp-sasylf/.vscode/extensions.json b/lsp-sasylf/.vscode/extensions.json new file mode 100644 index 00000000..af515502 --- /dev/null +++ b/lsp-sasylf/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/lsp-sasylf/.vscode/launch.json b/lsp-sasylf/.vscode/launch.json new file mode 100644 index 00000000..7a32478b --- /dev/null +++ b/lsp-sasylf/.vscode/launch.json @@ -0,0 +1,31 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +{ + "version": "0.2.0", + "configurations": [ + { + "type": "extensionHost", + "request": "launch", + "name": "Launch Client", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}"], + "outFiles": ["${workspaceRoot}/client/out/**/*.js"], + "autoAttachChildProcesses": true, + "preLaunchTask": { + "type": "npm", + "script": "watch" + } + }, + { + "name": "Language Server E2E Test", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceRoot}", + "--extensionTestsPath=${workspaceRoot}/client/out/test/index", + "${workspaceRoot}/client/testFixture" + ], + "outFiles": ["${workspaceRoot}/client/out/test/**/*.js"] + } + ] +} diff --git a/lsp-sasylf/.vscode/settings.json b/lsp-sasylf/.vscode/settings.json new file mode 100644 index 00000000..390d2993 --- /dev/null +++ b/lsp-sasylf/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.insertSpaces": false, + "typescript.tsc.autoDetect": "off", + "typescript.preferences.quoteStyle": "single", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + } +} \ No newline at end of file diff --git a/lsp-sasylf/.vscode/tasks.json b/lsp-sasylf/.vscode/tasks.json new file mode 100644 index 00000000..070d88eb --- /dev/null +++ b/lsp-sasylf/.vscode/tasks.json @@ -0,0 +1,33 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "compile", + "group": "build", + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc" + ] + }, + { + "type": "npm", + "script": "watch", + "isBackground": true, + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "panel": "dedicated", + "reveal": "never" + }, + "problemMatcher": [ + "$tsc-watch" + ] + } + ] +} \ No newline at end of file diff --git a/lsp-sasylf/.vscodeignore b/lsp-sasylf/.vscodeignore new file mode 100644 index 00000000..2a6b6a99 --- /dev/null +++ b/lsp-sasylf/.vscodeignore @@ -0,0 +1,15 @@ +.vscode/** +**/*.ts +**/*.map +.gitignore +**/tsconfig.json +**/tsconfig.base.json +contributing.md +.travis.yml +client/node_modules/** +!client/node_modules/vscode-jsonrpc/** +!client/node_modules/vscode-languageclient/** +!client/node_modules/vscode-languageserver-protocol/** +!client/node_modules/vscode-languageserver-types/** +!client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** +!client/node_modules/{semver,lru-cache,yallist}/** \ No newline at end of file diff --git a/lsp-sasylf/LICENSE.md b/lsp-sasylf/LICENSE.md new file mode 100644 index 00000000..3c943032 --- /dev/null +++ b/lsp-sasylf/LICENSE.md @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2009 by Jonathan Aldrich +Additional contributions by John Boyland and others + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lsp-sasylf/client/package-lock.json b/lsp-sasylf/client/package-lock.json new file mode 100644 index 00000000..4b06ed50 --- /dev/null +++ b/lsp-sasylf/client/package-lock.json @@ -0,0 +1,905 @@ +{ + "name": "sasylf-client", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "sasylf-client", + "version": "0.0.1", + "license": "MIT", + "dependencies": { + "vscode-languageclient": "^7.0.0" + }, + "devDependencies": { + "@types/vscode": "^1.63.0", + "@vscode/test-electron": "^2.1.2" + }, + "engines": { + "vscode": "^1.63.0" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/vscode": { + "version": "1.75.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.1.tgz", + "integrity": "sha512-emg7wdsTFzdi+elvoyoA+Q8keEautdQHyY5LNmHVM4PTpY8JgOTVADrGVyXGepJ6dVW2OS5/xnLUWh+nZxvdiA==", + "dev": true + }, + "node_modules/@vscode/test-electron": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.3.tgz", + "integrity": "sha512-7DmdGYQTqRNaLHKG3j56buc9DkstriY4aV0S3Zj32u0U9/T0L8vwWAC9QGCh1meu1VXDEla1ze27TkqysHGP0Q==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", + "engines": { + "node": ">=8.0.0 || >=10.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + }, + "engines": { + "vscode": "^1.52.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "dependencies": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + }, + "dependencies": { + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/vscode": { + "version": "1.75.1", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.75.1.tgz", + "integrity": "sha512-emg7wdsTFzdi+elvoyoA+Q8keEautdQHyY5LNmHVM4PTpY8JgOTVADrGVyXGepJ6dVW2OS5/xnLUWh+nZxvdiA==", + "dev": true + }, + "@vscode/test-electron": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.2.3.tgz", + "integrity": "sha512-7DmdGYQTqRNaLHKG3j56buc9DkstriY4aV0S3Zj32u0U9/T0L8vwWAC9QGCh1meu1VXDEla1ze27TkqysHGP0Q==", + "dev": true, + "requires": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true + }, + "unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" + }, + "vscode-languageclient": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-7.0.0.tgz", + "integrity": "sha512-P9AXdAPlsCgslpP9pRxYPqkNYV7Xq8300/aZDpO35j1fJm/ncize8iGswzYlcvFw5DQUx4eVk+KvfXdL0rehNg==", + "requires": { + "minimatch": "^3.0.4", + "semver": "^7.3.4", + "vscode-languageserver-protocol": "3.16.0" + } + }, + "vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "requires": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/lsp-sasylf/client/package.json b/lsp-sasylf/client/package.json new file mode 100644 index 00000000..e1032313 --- /dev/null +++ b/lsp-sasylf/client/package.json @@ -0,0 +1,22 @@ +{ + "name": "sasylf-client", + "description": "VSCode part of a language server", + "author": "Microsoft Corporation", + "license": "MIT", + "version": "0.0.1", + "publisher": "vscode", + "repository": { + "type": "git", + "url": "https://github.com/Microsoft/vscode-extension-samples" + }, + "engines": { + "vscode": "^1.63.0" + }, + "dependencies": { + "vscode-languageclient": "^7.0.0" + }, + "devDependencies": { + "@types/vscode": "^1.63.0", + "@vscode/test-electron": "^2.1.2" + } +} diff --git a/lsp-sasylf/client/src/extension.ts b/lsp-sasylf/client/src/extension.ts new file mode 100644 index 00000000..ba7c4746 --- /dev/null +++ b/lsp-sasylf/client/src/extension.ts @@ -0,0 +1,124 @@ +import * as path from "path"; +import { + workspace, + ExtensionContext, + TextDocumentContentProvider, + Uri, + window, + commands, +} from "vscode"; + +import { + LanguageClient, + LanguageClientOptions, + ServerOptions, + TransportKind, +} from "vscode-languageclient/node"; + +import { readFileSync, existsSync } from "fs"; +import { spawn, spawnSync } from "child_process"; + +let client: LanguageClient; + +function validateHandler(_: any[]) { + const file = window.activeTextEditor?.document.uri.toString(); + + if (!file) return; + + client.sendNotification("custom/validateTextDocument", file); +} + +function appHandler(_: any[]) { + const text = window.activeTextEditor?.document.getText(); + + if (!text) return; + + process.chdir(`${__dirname}/../../electron_app`); + + if (!existsSync(`node_modules`)) { + spawnSync("npm", ["install"]); + } + + client.sendRequest("custom/getAST").then((ast) => { + var spawn_env = JSON.parse(JSON.stringify(process.env)); + delete spawn_env.ATOM_SHELL_INTERNAL_RUN_AS_NODE; + delete spawn_env.ELECTRON_RUN_AS_NODE; + + spawn( + "npm", + [ + "run", + "start", + "--", + "--", + JSON.stringify(ast), + window.activeTextEditor?.document.fileName || "", + ], + { env: spawn_env, detached: true }, + ); + }); +} + +export function activate(context: ExtensionContext) { + // The server is implemented in node + const serverModule = context.asAbsolutePath( + path.join("server", "out", "server.js"), + ); + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + const serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { + module: serverModule, + transport: TransportKind.ipc, + }, + }; + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for plain text documents + documentSelector: [{ scheme: "file", language: "sasylf" }], + synchronize: { + // Notify the server about file changes to '.clientrc files contained in the workspace + fileEvents: workspace.createFileSystemWatcher("**/.clientrc"), + }, + }; + + const myScheme = "temporary"; + const myProvider = new (class implements TextDocumentContentProvider { + provideTextDocumentContent(uri: Uri): string { + return readFileSync(uri.fsPath, "utf8"); + } + })(); + + context.subscriptions.push( + workspace.registerTextDocumentContentProvider(myScheme, myProvider), + ); + + context.subscriptions.push( + commands.registerCommand("sasylf.Validate", validateHandler), + ); + + context.subscriptions.push( + commands.registerCommand("sasylf.App", appHandler), + ); + + // Create the language client and start the client. + client = new LanguageClient( + "languageServerExample", + "Language Server Example", + serverOptions, + clientOptions, + ); + + console.log("Starting client"); + client.start(); +} + +export function deactivate(): Thenable | undefined { + if (!client) { + return undefined; + } + return client.stop(); +} diff --git a/lsp-sasylf/client/tsconfig.json b/lsp-sasylf/client/tsconfig.json new file mode 100644 index 00000000..2c7ebf34 --- /dev/null +++ b/lsp-sasylf/client/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es2021", + "lib": ["es2021"], + "outDir": "out", + "rootDir": "src", + "sourceMap": true + }, + "include": ["src"], + "exclude": ["node_modules", ".vscode-test"] +} diff --git a/lsp-sasylf/defaultTheme.json b/lsp-sasylf/defaultTheme.json new file mode 100644 index 00000000..0549d925 --- /dev/null +++ b/lsp-sasylf/defaultTheme.json @@ -0,0 +1,55 @@ +{ + "name": "Default Theme", + "tokenColors": [ + { + "name": "Comment Light Theme", + "scope": "comment", + "settings": { + "foreground": "#008000" + }, + "type": "light" + }, + { + "name": "Comment Dark Theme", + "scope": "comment", + "settings": { + "foreground": "#5FAF5F" + }, + "type": "dark" + }, + { + "name": "Keyword Light Theme", + "scope": "keyword", + "settings": { + "foreground": "#000080", + "fontStyle": "bold" + }, + "type": "light" + }, + { + "name": "Keyword Dark Theme", + "scope": "keyword", + "settings": { + "foreground": "#7FAFE1", + "fontStyle": "bold" + }, + "type": "dark" + }, + { + "name": "Support Light Theme", + "scope": "support", + "settings": { + "foreground": "#7F0055" + }, + "type": "light" + }, + { + "name": "Support Dark Theme", + "scope": "support", + "settings": { + "foreground": "#AF5F7F" + }, + "type": "dark" + } + ] +} diff --git a/lsp-sasylf/electron_app/.eslintrc.json b/lsp-sasylf/electron_app/.eslintrc.json new file mode 100644 index 00000000..2d7aa607 --- /dev/null +++ b/lsp-sasylf/electron_app/.eslintrc.json @@ -0,0 +1,16 @@ +{ + "env": { + "browser": true, + "es6": true, + "node": true + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/electron", + "plugin:import/typescript" + ], + "parser": "@typescript-eslint/parser" +} diff --git a/lsp-sasylf/electron_app/.gitignore b/lsp-sasylf/electron_app/.gitignore new file mode 100644 index 00000000..8296128d --- /dev/null +++ b/lsp-sasylf/electron_app/.gitignore @@ -0,0 +1,92 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock +.DS_Store + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env +.env.test + +# parcel-bundler cache (https://parceljs.org/) +.cache + +# next.js build output +.next + +# nuxt.js build output +.nuxt + +# vuepress build output +.vuepress/dist + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# Webpack +.webpack/ + +# Vite +.vite/ + +# Electron-Forge +out/ diff --git a/lsp-sasylf/electron_app/SASyLF.jar b/lsp-sasylf/electron_app/SASyLF.jar new file mode 100644 index 00000000..e6784457 Binary files /dev/null and b/lsp-sasylf/electron_app/SASyLF.jar differ diff --git a/lsp-sasylf/electron_app/forge.config.ts b/lsp-sasylf/electron_app/forge.config.ts new file mode 100644 index 00000000..351b3015 --- /dev/null +++ b/lsp-sasylf/electron_app/forge.config.ts @@ -0,0 +1,39 @@ +import type { ForgeConfig } from '@electron-forge/shared-types'; +import { MakerSquirrel } from '@electron-forge/maker-squirrel'; +import { MakerZIP } from '@electron-forge/maker-zip'; +import { MakerDeb } from '@electron-forge/maker-deb'; +import { MakerRpm } from '@electron-forge/maker-rpm'; +import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives'; +import { WebpackPlugin } from '@electron-forge/plugin-webpack'; + +import { mainConfig } from './webpack.main.config'; +import { rendererConfig } from './webpack.renderer.config'; + +const config: ForgeConfig = { + packagerConfig: { + asar: true, + }, + rebuildConfig: {}, + makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})], + plugins: [ + new AutoUnpackNativesPlugin({}), + new WebpackPlugin({ + mainConfig, + renderer: { + config: rendererConfig, + entryPoints: [ + { + html: './src/index.html', + js: './src/renderer.ts', + name: 'main_window', + preload: { + js: './src/preload.ts', + }, + }, + ], + }, + }), + ], +}; + +export default config; diff --git a/lsp-sasylf/electron_app/lib/jackson-annotations-2.14.2.jar b/lsp-sasylf/electron_app/lib/jackson-annotations-2.14.2.jar new file mode 100644 index 00000000..7f52c92f Binary files /dev/null and b/lsp-sasylf/electron_app/lib/jackson-annotations-2.14.2.jar differ diff --git a/lsp-sasylf/electron_app/lib/jackson-core-2.14.2.jar b/lsp-sasylf/electron_app/lib/jackson-core-2.14.2.jar new file mode 100644 index 00000000..53c1b16a Binary files /dev/null and b/lsp-sasylf/electron_app/lib/jackson-core-2.14.2.jar differ diff --git a/lsp-sasylf/electron_app/lib/jackson-databind-2.14.2.jar b/lsp-sasylf/electron_app/lib/jackson-databind-2.14.2.jar new file mode 100644 index 00000000..a1cebc30 Binary files /dev/null and b/lsp-sasylf/electron_app/lib/jackson-databind-2.14.2.jar differ diff --git a/lsp-sasylf/electron_app/package.json b/lsp-sasylf/electron_app/package.json new file mode 100644 index 00000000..3e60112e --- /dev/null +++ b/lsp-sasylf/electron_app/package.json @@ -0,0 +1,67 @@ +{ + "name": "electron_app", + "productName": "electron_app", + "version": "1.0.0", + "description": "My Electron application description", + "main": ".webpack/main", + "scripts": { + "start": "electron-forge start", + "package": "electron-forge package", + "make": "electron-forge make", + "publish": "electron-forge publish", + "lint": "eslint --ext .ts,.tsx ." + }, + "keywords": [], + "author": { + "name": "rdasanoor", + "email": "rdasanoor@gmail.com" + }, + "license": "MIT", + "devDependencies": { + "@electron-forge/cli": "^7.2.0", + "@electron-forge/maker-deb": "^7.2.0", + "@electron-forge/maker-rpm": "^7.2.0", + "@electron-forge/maker-squirrel": "^7.2.0", + "@electron-forge/maker-zip": "^7.2.0", + "@electron-forge/plugin-auto-unpack-natives": "^7.2.0", + "@electron-forge/plugin-webpack": "^7.2.0", + "@types/bootstrap": "^5.2.10", + "@types/react": "^18.2.45", + "@types/react-dom": "^18.2.18", + "@typescript-eslint/eslint-plugin": "^5.62.0", + "@typescript-eslint/parser": "^5.62.0", + "@vercel/webpack-asset-relocator-loader": "1.7.3", + "autoprefixer": "^10.4.16", + "css-loader": "^6.8.1", + "electron": "^29.1.0", + "eslint": "^8.56.0", + "eslint-plugin-import": "^2.29.1", + "fork-ts-checker-webpack-plugin": "^7.3.0", + "node-loader": "^2.0.0", + "postcss-loader": "^7.3.3", + "sass": "^1.69.5", + "sass-loader": "^13.3.2", + "style-loader": "^3.3.3", + "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", + "typescript": "~4.5.4" + }, + "dependencies": { + "@dnd-kit/core": "^6.1.0", + "@dnd-kit/modifiers": "^7.0.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.10", + "bootstrap": "^5.3.2", + "bootstrap-icons": "^1.11.2", + "clsx": "^2.1.0", + "electron-squirrel-startup": "^1.0.0", + "react": "^18.2.0", + "react-bootstrap": "^2.9.1", + "react-contexify": "^5.0.1-1", + "react-dom": "^18.2.0", + "react-icons": "^4.12.0", + "react-tabs": "^6.0.2", + "react-transition-group": "^4.4.5", + "react-zoom-pan-pinch": "^3.3.0" + } +} diff --git a/lsp-sasylf/electron_app/src/app.tsx b/lsp-sasylf/electron_app/src/app.tsx new file mode 100644 index 00000000..e0da52cc --- /dev/null +++ b/lsp-sasylf/electron_app/src/app.tsx @@ -0,0 +1,339 @@ +import React, { useState, useRef, useEffect, RefObject } from "react"; +import { createRoot } from "react-dom/client"; +import { ast, tab, input, canvasState } from "./types"; +import Bank from "./components/bank"; +import ProofArea from "./components/proof"; +import Canvas from "./components/canvas"; +import Export from "./components/export"; +import Input from "./components/input"; +import { NodeContext, FileContext } from "./components/state"; +import { DndContext, DragEndEvent, DragStartEvent } from "@dnd-kit/core"; +import { snapCenterToCursor } from "@dnd-kit/modifiers"; +import Tab from "react-bootstrap/Tab"; +import Nav from "react-bootstrap/Nav"; +import CloseButton from "react-bootstrap/CloseButton"; +import Card from "react-bootstrap/Card"; +import Button from "react-bootstrap/Button"; +import { DragOverlay } from "@dnd-kit/core"; +import { getTree } from "./components/utils"; +import { useContextMenu } from "react-contexify"; +import ContextMenu from "./components/menu"; + +export default function MyApp() { + const [tabs, setTabs] = useState([]); + const [activeText, setActiveText] = useState(null); + const [showExport, setShowExport] = useState(false); + const [showInput, setShowInput] = useState(false); + const [activeKey, setActiveKey] = useState(0); + const [refs, setRefs] = useState>>( + {}, + ); + const [show, setShow] = useState(false); + const [canvasStates, setCanvasStates] = useState([]); + const [offsetWidth, setOffsetWidth] = useState(0); + + const shiftRef = useRef(false); + const proofRef = useRef(null); + const bankRef = useRef(null); + const { show: showContextMenu } = useContextMenu({ id: "3" }); + + const updateOffsetWidth = () => setOffsetWidth(bankRef.current!.scrollWidth); + + useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if (event.key === "Shift") shiftRef.current = true; + }; + const handleKeyUp = (event: KeyboardEvent) => { + if (event.key === "Shift") shiftRef.current = false; + }; + + document.addEventListener("keydown", handleKeyDown); + document.addEventListener("keyup", handleKeyUp); + + const resizeListener = (_: Event) => updateOffsetWidth(); + + document.addEventListener("resize", resizeListener); + + return () => { + document.removeEventListener("keydown", handleKeyDown); + document.removeEventListener("keyup", handleKeyUp); + document.removeEventListener("resize", resizeListener); + }; + }, []); + + const addTab = ( + compUnit: ast | null, + name: string | null, + file: string, + unicode: string[], + ) => { + const maxId = Math.max(-1, ...tabs.map((element) => element.id)); + setTabs( + tabs.concat([ + { + ast: compUnit, + id: maxId + 1, + name, + inputs: [], + file, + unicode, + } as tab, + ]), + ); + setCanvasStates( + canvasStates.concat([{ x: 0, y: 0, scale: 1, id: maxId + 1 }]), + ); + setActiveKey(maxId + 1); + }; + + useEffect(() => { + (window as any).electronAPI.addAST( + ({ + compUnit, + name, + file, + unicode, + }: { + compUnit: ast | null; + name: string | null; + file: string; + unicode: string[]; + }) => addTab(compUnit, name, file, unicode), + ); + + (window as any).electronAPI.showModal(() => { + setShowExport(true); + }); + }, [tabs]); + + const addRef = (id: number, ref: RefObject) => + setRefs({ + ...refs, + [id]: ref, + }); + + const handleDragStart = (event: DragStartEvent) => + setActiveText(event.active.data.current?.text); + + const deleteInput = (activeKey: number, ind: number) => { + const newTabs: tab[] = tabs.map((tab) => JSON.parse(JSON.stringify(tab))); + + for (const tab of newTabs) + if (tab.id == activeKey) tab.inputs.splice(ind, 1); + + setTabs(newTabs); + }; + + const appendInput = (activeKey: number, inp: input) => { + const newTabs: tab[] = tabs.map((tab) => JSON.parse(JSON.stringify(tab))); + + for (const tab of newTabs) if (tab.id == activeKey) tab.inputs.push(inp); + + setTabs(newTabs); + }; + + const handleDragEnd = (event: DragEndEvent) => { + const { active, over } = event; + const activeData = active.data.current; + setActiveText(null); + + if (!over) return; + + const overData = over.data.current; + const overType = overData?.type; + const activeType = activeData?.type; + + if (overType === "rule" && activeType === "rule") { + const event = new CustomEvent("rule", { + detail: { + overId: over.id, + text: activeData?.text, + }, + }); + document.dispatchEvent(event); + + if (shiftRef.current && activeData?.ind != null) + deleteInput(activeKey, activeData?.ind); + } + if ( + activeType === "node" && + ((overType === "copy" && activeData?.text === overData?.text) || + overType === "topdown") + ) { + const which = overType === "topdown"; + const event = new CustomEvent(which ? "topdown-tree" : "tree", { + detail: { + tree: getTree(refs[active.id as number].current), + overId: over.id, + text: which ? activeData?.text : "", + }, + }); + document.dispatchEvent(event); + + if (shiftRef.current && activeData?.ind != null) + deleteInput(activeKey, activeData?.ind); + } + if (activeType === "rule" && overType === "topdown-rule") { + const event = new CustomEvent("topdown-rule", { + detail: { + overId: over.id, + text: activeData?.text, + }, + }); + document.dispatchEvent(event); + } + }; + + function handleCloseTab(id: number) { + const newTabs = tabs.filter((element) => element.id !== id); + setTabs(newTabs); + setCanvasStates(canvasStates.filter((element) => element.id !== id)); + + if (newTabs.length) setActiveKey(newTabs[0].id); + else { + setActiveKey(0); + setShow(false); + } + } + + const onSelect = (eventKey: string | null) => + setActiveKey(eventKey ? Number(eventKey) : 0); + + useEffect(() => { + if (show) updateOffsetWidth(); + }, [activeKey]); + + return ( + +
+ { + setShow(!show); + updateOffsetWidth(); + }} + compUnit={tabs.find((value) => value.id === activeKey)?.ast} + bankRef={bankRef} + width={offsetWidth} + setWidth={setOffsetWidth} + /> +
+ + + {tabs.map((element, index) => ( + + + + { + showContextMenu({ + event: event, + props: { nodeId }, + position: { + x: event.clientX, + y: event.clientY, + }, + }); + }, + }} + > + + {element.id === activeKey ? ( + + deleteInput(element.id, ind) + } + /> + ) : ( + + deleteInput(element.id, ind) + } + /> + )} + + + + + setShowInput(false)} + inputs={element.inputs} + unicode={element.unicode} + appendHandler={(inp: input) => appendInput(element.id, inp)} + /> + + setShowExport(false)} + proofRef={proofRef} + inputs={element.inputs} + /> + + ))} + +
+ + + {activeText ? ( + + {activeText} + + ) : null} + +
+
+ ); +} + +const appContainer = document.createElement("div"); +document.body.appendChild(appContainer); + +const root = createRoot(appContainer); +root.render(); diff --git a/lsp-sasylf/electron_app/src/components/bank.tsx b/lsp-sasylf/electron_app/src/components/bank.tsx new file mode 100644 index 00000000..315b7af5 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/bank.tsx @@ -0,0 +1,231 @@ +import React, { useState } from "react"; +import Button from "react-bootstrap/Button"; +import CloseButton from "react-bootstrap/CloseButton"; +import Collapse from "react-bootstrap/Collapse"; +import { FaArrowRightLong } from "react-icons/fa6"; +import { ast, judgmentNode, ruleNode, theoremNode, moduleNode } from "../types"; +import Draggable from "./draggable"; + +interface RuleLikeProps { + text: React.JSX.Element; + className?: string; +} + +function RuleLike(props: RuleLikeProps) { + return ( + + ); +} + +function BankCollapse({ + children, + name, + variant, + title, +}: { + children: React.ReactNode; + name: string; + variant: string; + title: string; +}) { + const [open, setOpen] = useState(false); + + const onChange = () => { + const event = new Event("resize"); + document.dispatchEvent(event); + }; + + return ( + <> + + +
{children}
+
+ + ); +} + +function Judgment(props: { judgment: judgmentNode }) { + const rules = props.judgment.rules.map((rule) => ruleToText(rule)); + const rulesElements = rules.map((rule, ind) => ( + + + + )); + + return ( + +
+
{rulesElements}
+
+
+ ); +} + +function Module(props: { module: moduleNode }) { + return ( + + + + ); +} + +function ruleToText(rule: ruleNode): [React.JSX.Element, string] { + let max_len = 0; + const lines: string[] = []; + + for (const premise of rule.premises) + max_len = Math.max(max_len, premise.length); + + max_len = Math.max(max_len, rule.conclusion.length); + + for (const premise of rule.premises) lines.push(premise); + + lines.push(`${"-".repeat(max_len)} ${rule.name}`); + lines.push(rule.conclusion); + + return [ + <> + {lines.map((line, ind) => ( +
+ {line} +
+
+ ))} + , + rule.name, + ]; +} + +function theoremToText(theorem: theoremNode): [React.JSX.Element, string] { + let max_len = 0; + const lines: string[] = []; + + for (const forall of theorem.foralls) + max_len = Math.max(max_len, forall.length); + + max_len = Math.max(max_len, theorem.conclusion.length); + + for (const forall of theorem.foralls) lines.push(forall); + + lines.push(`${"-".repeat(max_len)} ${theorem.name}`); + lines.push(theorem.conclusion); + + return [ + <> + {lines.map((line, ind) => ( +
+ {line} +
+
+ ))} + , + theorem.name, + ]; +} + +function RuleLikes(props: { + compUnit: ast; + bankRef?: React.RefObject; +}) { + const theorems = props.compUnit.theorems.map((value) => theoremToText(value)); + + const theoremsElements = theorems.map((thm, ind) => ( + + + + )); + + const judgments = props.compUnit.judgments.map((value, ind) => ( + + )); + + const modules = props.compUnit.modules.map((value, ind) => ( + + )); + + return ( +
+ {theoremsElements} + {judgments} + {modules} +
+ ); +} + +interface BankProps { + compUnit: ast | undefined; + toggleShow: () => void; + bankRef: React.RefObject; + width: number; + setWidth: React.Dispatch>; +} + +export default function Bank(props: BankProps) { + const [isResizing, setIsResizing] = useState(false); + + const handleClose = () => props.setWidth(0); + const handleShow = () => props.setWidth(props.bankRef.current!.scrollWidth); + + const startResizing = (_: React.MouseEvent) => setIsResizing(true); + const stopResizing = () => setIsResizing(false); + + const resize = (e: MouseEvent) => { + if (isResizing) + props.setWidth( + e.clientX - props.bankRef.current!.getBoundingClientRect().left, + ); + }; + + React.useEffect(() => { + window.addEventListener("mousemove", resize); + window.addEventListener("mouseup", stopResizing); + + return () => { + window.removeEventListener("mousemove", resize); + window.removeEventListener("mouseup", stopResizing); + }; + }, [resize, stopResizing]); + + return props.compUnit ? ( + <> + +
+
+

Rules Bank

+ +
+ +
+
+ + ) : null; +} diff --git a/lsp-sasylf/electron_app/src/components/canvas.tsx b/lsp-sasylf/electron_app/src/components/canvas.tsx new file mode 100644 index 00000000..d964a1f3 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/canvas.tsx @@ -0,0 +1,126 @@ +import React, { useEffect, RefObject } from "react"; +import { + TransformWrapper, + TransformComponent, + useTransformInit, + useTransformEffect, +} from "react-zoom-pan-pinch"; + +import ListGroup from "react-bootstrap/ListGroup"; +import { input } from "../types"; + +const TransformComponentWrapper = (props: { + children: any; + index: number; + canvasStates: any; + setCanvasStates: any; + setTransform: any; +}) => { + useTransformInit((_) => { + const { scale, x, y } = props.canvasStates[props.index]; + props.setTransform(x, y, scale, 0); + }); + + useTransformEffect(({ state, instance }) => { + const newCanvasStates = [...props.canvasStates]; + newCanvasStates[props.index] = { + scale: state.scale, + x: state.positionX, + y: state.positionY, + id: props.canvasStates[props.index].id, + }; + props.setCanvasStates(newCanvasStates); + }); + return props.children; +}; + +function TheoremList(props: { + proofRef: RefObject; + zoomToElement: any; +}) { + if (props.proofRef.current == null) return <>; + const rootNodes = props.proofRef.current.getElementsByClassName("root-node"); + const n = rootNodes.length; + const conclusions: string[] = Array(rootNodes.length); + for (let i = 0; i < n; ++i) { + const proofNode = rootNodes[i]; + const containers = proofNode.getElementsByClassName("conclusion"); + const container = containers[containers.length - 1]; + const conclusion = container.getElementsByTagName("span")[0].textContent; + conclusions[i] = conclusion; + } + return ( + + {Array.from(Array(n).keys()).map((i, ind) => ( + props.zoomToElement(rootNodes[i])} + > + {conclusions[i]} + + ))} + + ); +} + +const Canvas = (props: { + children: any; + index: number; + canvasStates: any; + setCanvasStates: any; + proofRef: RefObject; + inputs: input[]; +}) => { + return ( +
+ + {({ zoomIn, zoomOut, resetTransform, setTransform, zoomToElement }) => ( + <> +
+ + + +
+ + + +
{props.children}
+
+
+ + )} +
+
+ ); +}; + +export default Canvas; diff --git a/lsp-sasylf/electron_app/src/components/draggable.tsx b/lsp-sasylf/electron_app/src/components/draggable.tsx new file mode 100644 index 00000000..5243ccdc --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/draggable.tsx @@ -0,0 +1,22 @@ +import React from "react"; +import { useDraggable } from "@dnd-kit/core"; +import { data } from "../types"; + +interface draggableProps { + id: number | string; + children: any; + data: data; +} + +export default function Draggable(props: draggableProps) { + const { attributes, listeners, setNodeRef } = useDraggable({ + id: props.id, + data: props.data, + }); + + return ( +
+ {props.children} +
+ ); +} diff --git a/lsp-sasylf/electron_app/src/components/droppable.tsx b/lsp-sasylf/electron_app/src/components/droppable.tsx new file mode 100644 index 00000000..12a52b9e --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/droppable.tsx @@ -0,0 +1,27 @@ +import React from "react"; +import { useDroppable } from "@dnd-kit/core"; +import { data } from "../types"; + +interface droppableProps { + id: number; + children: any; + className: string; + data?: data; +} + +export default function Droppable(props: droppableProps) { + const { isOver, setNodeRef } = useDroppable({ + id: props.id, + data: props.data, + }); + const style = { + opacity: isOver ? 0.5 : 1, + color: isOver ? "green" : "black", + }; + + return ( +
+ {props.children} +
+ ); +} diff --git a/lsp-sasylf/electron_app/src/components/error.tsx b/lsp-sasylf/electron_app/src/components/error.tsx new file mode 100644 index 00000000..bd5eba19 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/error.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import Modal from "react-bootstrap/Modal"; + +export default function ErrorModal({ + show, + text, + toggleShow, +}: { + show: boolean; + text: string; + toggleShow: () => void; +}) { + return ( + + + Invalid Operation + + {text} + + ); +} diff --git a/lsp-sasylf/electron_app/src/components/export.tsx b/lsp-sasylf/electron_app/src/components/export.tsx new file mode 100644 index 00000000..184bf2ac --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/export.tsx @@ -0,0 +1,112 @@ +import React from "react"; +import Form from "react-bootstrap/Form"; +import InputGroup from "react-bootstrap/InputGroup"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; +import { createTheorems } from "./utils"; +import { input } from "../types"; + +interface ExportProps { + show: boolean; + onHide: () => void; + proofRef: React.RefObject; + inputs: input[]; +} + +export default function Export(props: ExportProps) { + const handleExport: React.FormEventHandler = (event) => { + event.preventDefault(); + const formData = new FormData(event.target as HTMLFormElement); + + const formJson = Object.fromEntries(formData.entries()); + const theoremNames: string[] = new Array(props.inputs.length).fill(""); + const quantifiers: string[] = new Array(props.inputs.length).fill(""); + const wanted: Set = new Set(); + for (const [key, value] of formData.entries()) { + let index: number = 0; + if (key.includes("index")) { + index = parseInt(key.replace("-index", ""), 10); + wanted.add(index); + } + if (key.includes("forall")) { + index = parseInt(key.replace("-forall", ""), 10); + quantifiers[index] = value as string; + } + if (key.includes("name")) { + index = parseInt(key.replace("-name", ""), 10); + theoremNames[index] = value as string; + } + } + const theorem = createTheorems( + theoremNames, + quantifiers, + wanted, + props.proofRef.current, + ); + if ("clipboard" in formJson) { + (window as any).electronAPI.addToClipboard(theorem); + } + if ("file" in formJson) { + (window as any).electronAPI.saveFile(theorem); + } + props.onHide(); + }; + + const rootNodes = props.proofRef.current?.getElementsByClassName("root-node"); + const rootNodesArray = rootNodes ? Array.from(rootNodes) : []; + + return ( + + + Export Derivations + + +
+ {rootNodesArray.map((rootNode, index) => { + const containers = rootNode.getElementsByClassName("conclusion"); + const container = containers[containers.length - 1]; + const conclusion = + container.getElementsByTagName("span")[0].textContent; + + return ( + + + {conclusion} + {rootNode.classList.contains("free") ? ( + + ) : ( + <> + )} + + + ); + })} + + + + +
+
+ ); +} diff --git a/lsp-sasylf/electron_app/src/components/input.tsx b/lsp-sasylf/electron_app/src/components/input.tsx new file mode 100644 index 00000000..45967ff4 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/input.tsx @@ -0,0 +1,160 @@ +import React from "react"; +import Form from "react-bootstrap/Form"; +import Button from "react-bootstrap/Button"; +import Modal from "react-bootstrap/Modal"; +import { input } from "../types"; + +interface InputProps { + show: boolean; + onHide: () => void; + inputs: input[]; + appendHandler: (inp: input) => void; + unicode: string[]; +} + +export default function Input(props: InputProps) { + const [selectedType, setSelectedType] = React.useState("Premises"); + const [numInputs, setNumInputs] = React.useState(1); + const inputRefs = React.useRef([]); + const [currentRefIndex, setCurrentRefIndex] = React.useState(0); + + const handleTypeChange = (event: React.ChangeEvent) => { + const newType = event.target.value; + if (newType == "Conclusion") { + setCurrentRefIndex(0); + } + setSelectedType(newType); + }; + + const handleAddUnicode = (character: string) => { + if (selectedType == "Conclusion") { + setCurrentRefIndex(0); + } + const curInput = inputRefs.current[currentRefIndex]; + const startPos = curInput.selectionStart!; + const endPos = curInput.selectionEnd!; + curInput.value = + curInput.value.substring(0, startPos) + + character + + curInput.value.substring(endPos); + // Reset selection range after insert character; + curInput.focus(); + curInput.setSelectionRange(startPos + 1, endPos + 1); + }; + + const handleExport: React.FormEventHandler = (event) => { + event.preventDefault(); + const formData = new FormData(event.target as HTMLFormElement); + + const formJson = Object.fromEntries(formData.entries()); + const free: boolean = formJson.hasOwnProperty("free"); + const type: string = formJson.type as string; + let input: string[] = Array(numInputs).fill(""); + + if (type === "Conclusion") input[0] = formJson.input as string; + + if (type === "Premises") + for (const [key, value] of formData.entries()) { + let index = 0; + if (key.includes("input")) { + index = parseInt(key.replace("input-", ""), 10); + input[index] = value as string; + } + } + + props.appendHandler({ + input, + free, + id: Math.max(-1, ...props.inputs.map((element) => element.id)) + 1, + type, + }); + + props.onHide(); + }; + + return ( + + + Create New Theorem + + +
+ + + + {props.unicode.map((char, ind) => ( + + ))} + + {selectedType === "Premises" && ( + /* Render Premises-specific form elements */ + <> + {Array.from(Array(numInputs).keys()).map((i, ind) => ( + (inputRefs.current[ind] = el)} + onFocus={() => setCurrentRefIndex(ind)} + /> + ))} + + + )} + + {selectedType === "Conclusion" && ( + /* Render Conclusion-specific form elements */ + (inputRefs.current[0] = el)} + onFocus={() => setCurrentRefIndex(0)} + /> + )} + + + + +
+
+ ); +} diff --git a/lsp-sasylf/electron_app/src/components/menu.tsx b/lsp-sasylf/electron_app/src/components/menu.tsx new file mode 100644 index 00000000..85385c2e --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/menu.tsx @@ -0,0 +1,65 @@ +import React, { useState } from "react"; +import { Menu, Item, ItemParams } from "react-contexify"; +import "react-contexify/dist/ReactContexify.css"; +import Modal from "react-bootstrap/Modal"; +import Form from "react-bootstrap/Form"; +import Button from "react-bootstrap/Button"; +import InputGroup from "react-bootstrap/InputGroup"; +import { Direction } from "../types"; + +function ContextMenu(props: { MENU_ID: string }) { + const [show, setShow] = useState(false); + const [nodeId, setNodeId] = useState(0); + + const handleItemClick = (event: ItemParams) => { + setNodeId(event.props.nodeId); + setShow(true); + }; + + const handleReplace: React.FormEventHandler = (event) => { + event.preventDefault(); + const formData = new FormData(event.target as HTMLFormElement); + const formJson = Object.fromEntries(formData.entries()); + + const nodeEvent = new CustomEvent("replace", { + detail: { nodeId, data: formJson, dir: Direction.Both }, + }); + document.dispatchEvent(nodeEvent); + + setShow(false); + }; + + return ( + <> + + Replace free variables + + + setShow(false)}> + + Specify Replacement + + + +
+ + Old + + + + + New + + + + +
+
+
+ + ); +} + +export default ContextMenu; diff --git a/lsp-sasylf/electron_app/src/components/proof.tsx b/lsp-sasylf/electron_app/src/components/proof.tsx new file mode 100644 index 00000000..10d02eae --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/proof.tsx @@ -0,0 +1,546 @@ +import React, { + useState, + useEffect, + useContext, + useRef, + RefObject, +} from "react"; +import Droppable from "./droppable"; +import CloseButton from "react-bootstrap/CloseButton"; +import { NodeContext, FileContext } from "./state"; +import Form from "react-bootstrap/Form"; +import Draggable from "./draggable"; +import { + deleteElement, + extractPremise, + getChildrenIds, + getParentId, + capitalize, +} from "./utils"; +import { line, input, ast, Direction } from "../types"; +import ErrorModal from "./error"; + +let nodeCounter = 2; +interface TopdownHandler { + fn: (id: number) => void; + level: boolean; + ind?: number; +} + +function Premises(props: { + args: string[]; + tree: line | null; + topdownHandler: TopdownHandler | null; +}) { + if (props.args.length <= 1) return null; + + return ( +
+ {props.args.slice(0, -1).map((arg, ind) => ( + + ))} +
+ ); +} + +const getNumPremises = (ast: ast | null, rule: string) => { + if (!ast) return 0; + + const thm = ast?.theorems.find((elem) => elem.name === rule); + if (thm) return thm.foralls.length; + + for (const judgment of ast.judgments) { + const rle = judgment.rules.find((elem) => elem.name === rule); + if (rle) return rle.premises.length; + } + + return 0; +}; + +function TopDownNode({ + prems, + deleteHandler, + className, +}: { + prems: string[]; + deleteHandler: () => void; + className: string; +}) { + const { ast } = useContext(NodeContext); + const file = useContext(FileContext); + + const [rule, setRule] = useState(null); + const [id, setId] = useState(0); + const [trees, setTrees] = useState([]); + const [premises, setPremises] = useState([]); + const [numUsed, setNumUsed] = useState(0); + const [conclusion, setConclusion] = useState(null); + const [tree, setTree] = useState(null); + const [showModal, setShowModal] = useState(false); + const [errorText, setErrorText] = useState(""); + + useEffect(() => { + setId(nodeCounter++); + nodeCounter++; + setPremises(prems); + setNumUsed(prems.length); + setTrees( + prems.map((value) => { + return { + conclusion: value, + name: "", + rule: "Put rule here", + premises: [], + }; + }), + ); + }, []); + + useEffect(() => { + const listener = (event: Event) => { + const detail = (event as CustomEvent).detail; + + if (detail.overId == id + 1) { + if (getNumPremises(ast, detail.text) < numUsed) { + setErrorText("Wrong number of premises for specified rule"); + setShowModal(true); + } else setRule(detail.text); + } + }; + + document.addEventListener("topdown-rule", listener); + + return () => document.removeEventListener("topdown-rule", listener); + }, [id, numUsed]); + + useEffect(() => { + const listener = (event: Event) => { + const detail = (event as CustomEvent).detail; + + if (detail.overId == id) { + setTrees([...trees, detail.tree]); + setPremises([...premises, detail.text]); + setNumUsed(numUsed + 1); + } + }; + + document.addEventListener("topdown-tree", listener); + + return () => document.removeEventListener("topdown-tree", listener); + }, [id, trees, premises]); + + useEffect(() => { + if (!rule) return; + + if (numUsed != getNumPremises(ast, rule)) { + setConclusion(null); + return; + } + + (window as any).electronAPI + .topdownParse({ premises }, rule, file) + .then((res: { conclusion?: string[]; errors?: string[] }) => { + if (res.conclusion) setConclusion(res.conclusion[0]); + else if (res.errors) { + setErrorText( + res.errors + .map((error) => capitalize(error.split("error: ")[1])) + .join("\n"), + ); + setShowModal(true); + } else { + setErrorText("Invalid rule"); + setShowModal(true); + } + }); + }, [numUsed, rule]); + + useEffect(() => { + if (!conclusion || !rule) return; + if (trees.filter((elem) => elem == null).length) return; + + setTree({ conclusion, name: "", rule, premises: trees }); + }, [conclusion]); + + const deletePremise = (ind: number) => { + setTrees(deleteElement(trees, ind)); + setPremises(deleteElement(premises, ind)); + setNumUsed(numUsed - 1); + }; + + return ( + <> + setShowModal(!showModal)} + /> + {conclusion ? ( + deletePremise(ind), + level: false, + }} + deleteHandler={deleteHandler} + topdownClose={() => { + setConclusion(null); + setRule(null); + }} + root + /> + ) : ( +
+ +
+
+ {premises.map((premise, ind) => ( +
+
+ {premise} deletePremise(ind)} /> +
+
+ ))} + +
Add premise
+
+
+
+ Conclusion pending +
+ +
+ {rule ? ( + <> + {rule} setRule(null)} /> + + ) : ( + "Put rule here" + )} +
+
+
+ )} + + ); +} + +function propagateUp( + node: Element | null, + data: { oldvar: string; newvar: string }, +) { + const parId = getParentId(node); + + if (parId < 0) return; + + const event = new CustomEvent("replace", { + detail: { nodeId: parId, data, dir: Direction.Up }, + }); + + document.dispatchEvent(event); +} + +function propagateDown( + node: Element | null, + data: { oldvar: string; newvar: string }, +) { + const childIds = getChildrenIds(node); + + for (const id of childIds) { + const event = new CustomEvent("replace", { + detail: { nodeId: id, data, dir: Direction.Down }, + }); + + document.dispatchEvent(event); + } +} + +interface nodeProps { + conclusion: string; + className?: string; + tree: line | null; + root?: boolean; + ind?: number; + topdownHandler?: TopdownHandler; + deleteHandler?: () => void; + topdownClose?: () => void; +} + +function ProofNode(props: nodeProps) { + const { addRef, showContextMenu } = useContext(NodeContext); + const file = useContext(FileContext); + + const [id, setId] = useState(0); + const [args, setArgs] = useState(null); + const [tree, setTree] = useState(null); + const [rule, setRule] = useState(null); + const [conclusion, setConclusion] = useState(""); + const [show, setShow] = useState(false); + const [errorText, setErrorText] = useState(""); + + const proofNodeRef = useRef(null); + + const safeSetTree = (tree: line | null) => + setTree(tree && tree.rule === "Put rule here" ? null : tree); + + useEffect(() => { + setId(nodeCounter++); + safeSetTree(props.tree); + addRef(nodeCounter++ - 1, proofNodeRef); + }, []); + + useEffect(() => setConclusion(props.conclusion), [props.conclusion]); + + useEffect(() => { + const listener = (event: Event) => { + const detail = (event as CustomEvent).detail; + + if (id === detail.overId) safeSetTree(detail.tree); + }; + + const ruleListener = (event: Event) => { + const detail = (event as CustomEvent).detail; + + if (id === detail.overId) if (!rule && !tree) setRule(detail.text); + }; + + const replaceListener = (event: Event) => { + const detail = (event as CustomEvent).detail; + + if (id === detail.nodeId) + (window as any).electronAPI + .substitute(conclusion, detail.data.oldvar, detail.data.newvar, file) + .then((res: { result?: string; errors?: string[] }) => { + if (Object.keys(res).length != 0) { + if (res.errors) { + setErrorText( + res.errors + .map((error) => capitalize(error.split("error: ")[1])) + .join("\n"), + ); + setShow(true); + } else if (res.result) { + setConclusion(res.result); + + if ([Direction.Both, Direction.Up].includes(detail.dir)) + propagateUp(proofNodeRef.current, detail.data); + + if ([Direction.Both, Direction.Down].includes(detail.dir)) + propagateDown(proofNodeRef.current, detail.data); + } + } + }); + }; + + document.addEventListener("tree", listener); + document.addEventListener("rule", ruleListener); + document.addEventListener("replace", replaceListener); + + return () => { + document.removeEventListener("tree", listener); + document.removeEventListener("rule", ruleListener); + document.removeEventListener("replace", replaceListener); + }; + }, [id, rule, tree, conclusion]); + + useEffect(() => { + if (rule && !tree) + (window as any).electronAPI + .parse(conclusion, rule, file) + .then((res: { arguments?: string[]; errors?: string[] }) => { + if (res.arguments) setArgs(res.arguments); + else if (res.errors) { + setErrorText( + res.errors + .map((error) => capitalize(error.split("error: ")[1])) + .join("\n"), + ); + setShow(true); + } else { + setErrorText("Invalid rule"); + setShow(true); + } + }); + else setArgs(null); + }, [rule]); + + useEffect(() => safeSetTree(props.tree), [props.tree]); + + return ( +
+ setShow(!show)} + /> + {props.root ? ( + (props.deleteHandler ? props.deleteHandler() : null)} + /> + ) : null} + + {props.topdownHandler && props.topdownHandler.level ? ( + + props.topdownHandler?.fn(props.topdownHandler.ind as number) + } + /> + ) : null} + +
+ {args ? ( + + ) : tree ? ( + value.conclusion), ""]} + tree={tree} + topdownHandler={ + props.topdownHandler && !props.topdownHandler.level + ? props.topdownHandler + : null + } + /> + ) : ( + +
Copy node here
+
+ )} +
+ +
+ + + + showContextMenu(event, id)} + > + {conclusion} + + +
+
+ + +
+ {tree || rule ? ( + <> + {tree ? tree.rule : rule}{" "} + { + if (props.topdownClose) props.topdownClose(); + else { + setRule(null); + setTree(null); + } + }} + /> + + ) : ( + "Put rule here" + )} +
+
+
+ ); +} + +export default function ProofArea(props: { + proofRef?: RefObject; + inputs: input[]; + deleteHandler: (ind: number) => void; +}) { + return ( + <> +
+ {props.inputs.map(({ input, free, id, type }, ind) => + type === "Conclusion" ? ( + { + props.deleteHandler(ind); + }} + root + /> + ) : ( + { + props.deleteHandler(ind); + }} + /> + ), + )} +
+ + ); +} diff --git a/lsp-sasylf/electron_app/src/components/state.tsx b/lsp-sasylf/electron_app/src/components/state.tsx new file mode 100644 index 00000000..4eab8d98 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/state.tsx @@ -0,0 +1,16 @@ +import { createContext, RefObject, MouseEventHandler } from "react"; +import { ast } from "../types"; + +type Context = { + addRef: (id: number, ref: RefObject) => void; + showContextMenu: (event: React.MouseEvent, nodeId: number) => void; + ast: ast | null; +}; + +export const NodeContext = createContext({ + addRef: (_, __) => {}, + showContextMenu: (_, __) => {}, + ast: null, +}); + +export const FileContext = createContext(""); diff --git a/lsp-sasylf/electron_app/src/components/utils.ts b/lsp-sasylf/electron_app/src/components/utils.ts new file mode 100644 index 00000000..620a5995 --- /dev/null +++ b/lsp-sasylf/electron_app/src/components/utils.ts @@ -0,0 +1,129 @@ +import { line } from "../types"; +import { readFileSync } from "fs"; + +export function capitalize(str: string) { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export const deleteElement = (arr: T[], index: number) => [ + ...arr.slice(0, index), + ...arr.slice(index + 1), +]; + +export function extractPremise(conclusion: string, tree: line) { + for (const premise of tree.premises) + if (premise.conclusion === conclusion) return premise; + + return null; +} + +export function getChildrenIds(proofNode: Element | null) { + const res: number[] = []; + + if (!proofNode) return res; + + const premiseNodes = proofNode.getElementsByClassName("premises"); + + for (const premise of premiseNodes) { + const containers = premise.getElementsByClassName("premise"); + const container = containers[containers.length - 1]; + res.push(parseInt(container.id)); + } + + return res; +} + +export function getParentId(proofNode: Element | null) { + if (!proofNode || proofNode.classList.contains("root-node")) return -1; + + const res = proofNode.parentElement?.parentElement?.parentElement?.id; + + return res ? parseInt(res) : -1; +} + +export function getTree(proofNode: Element | null): line | null { + if (!proofNode) return null; + + const containers = proofNode.getElementsByClassName("conclusion"); + const container = containers[containers.length - 1]; + const conclusion = container.getElementsByTagName("span")[0].textContent; + const name = container.getElementsByTagName("input")[0].value; + const rules = proofNode.getElementsByClassName("rule"); + const rule = rules[rules.length - 1].textContent; + + let premises: line[] = []; + const premiseNodes = proofNode.getElementsByClassName("premises"); + + for (const premise of premiseNodes) { + const prev = getTree(premise); + + if (prev) premises.push(prev); + } + + if (!conclusion || !rule) return null; + + return { conclusion, name, rule, premises }; +} + +type lineText = { + conclusion: string; + name: string; + rule: string; + premises: line[]; +}; + +function theoremHelper(root: Element | null): lineText[] { + if (!root) return []; + + const tree = getTree(root); + + if (!tree) return []; + + function bfs(l: line) { + let res: line[] = [l]; + + for (const premise of l.premises) res = bfs(premise).concat([l]); + + return res; + } + + return bfs(tree); +} + +export function createTheorem( + theoremName: string, + quantifiers: string, + rootNode: Element | null, +) { + const nodes = theoremHelper(rootNode); + + if (!nodes) return; + + const conclusion = nodes[nodes.length - 1].conclusion; + let content = `theorem ${theoremName.trim()} : ${quantifiers.trim()} exists ${conclusion.trim()}.\n`; + for (const node of nodes) { + content += `${node.name.trim()}: ${node.conclusion.trim()} by rule ${node.rule.trim()}${ + node.premises.length === 0 + ? "" + : ` on ${node.premises.map((premise) => premise.name).join(", ")}` + }\n`; + } + return content; +} + +export function createTheorems( + theoremNames: string[], + quantifiers: string[], + wanted: Set, + dom: HTMLDivElement | null, +): string { + const rootNodes = dom?.getElementsByClassName("root-node"); + // TODO : assert len of rootNodes and theoremNames + if (!rootNodes) return ""; + let content: string = ""; + for (let i = 0; i < rootNodes.length; ++i) { + if (wanted.has(i)) + content += createTheorem(theoremNames[i], quantifiers[i], rootNodes[i]); + } + return content; +} diff --git a/lsp-sasylf/electron_app/src/index.html b/lsp-sasylf/electron_app/src/index.html new file mode 100644 index 00000000..7126ddda --- /dev/null +++ b/lsp-sasylf/electron_app/src/index.html @@ -0,0 +1,9 @@ + + + + + + App + + + diff --git a/lsp-sasylf/electron_app/src/index.scss b/lsp-sasylf/electron_app/src/index.scss new file mode 100644 index 00000000..ab5af1a0 --- /dev/null +++ b/lsp-sasylf/electron_app/src/index.scss @@ -0,0 +1,15 @@ +@import "~bootstrap/scss/bootstrap"; +@import "styles/bank"; +@import "styles/proof"; +@import "styles/canvas"; +@import "styles/close"; +@import "styles/app"; + +html, +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, + Arial, sans-serif; + margin: auto; + height: 100%; + padding: 1rem; +} diff --git a/lsp-sasylf/electron_app/src/index.ts b/lsp-sasylf/electron_app/src/index.ts new file mode 100644 index 00000000..0c6f9764 --- /dev/null +++ b/lsp-sasylf/electron_app/src/index.ts @@ -0,0 +1,258 @@ +import { + app, + session, + BrowserWindow, + Menu, + MenuItem, + dialog, + ipcMain, + OpenDialogOptions, + IpcMainInvokeEvent, + clipboard, +} from "electron"; +import { spawnSync } from "child_process"; +import { ast } from "./types"; +import { PathOrFileDescriptor, readFile, readFileSync, writeFile } from "fs"; +import path from "node:path"; +import os from "node:os"; + +// This allows TypeScript to pick up the magic constants that's auto-generated by Forge's Webpack +// plugin that tells the Electron app where to look for the Webpack-bundled app code (depending on +// whether you're running in development or production). +declare const MAIN_WINDOW_WEBPACK_ENTRY: string; +declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; + +// Handle creating/removing shortcuts on Windows when installing/uninstalling. +if (require("electron-squirrel-startup")) app.quit(); + +const createWindow = (): BrowserWindow => { + const mainWindow = new BrowserWindow({ + height: 600, + width: 800, + webPreferences: { + preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, + }, + }); + + mainWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); + return mainWindow; +}; + +const getUnicode = (file: string) => { + const data = readFileSync(file).toString(); + + return Array.from( + new Set(data.split("").filter((char) => char.charCodeAt(0) > 127)), + ); +}; + +function handleUpload(mainWindow: BrowserWindow, filePath: string) { + const command = spawnSync( + `java -jar ${__dirname}/../../SASyLF.jar`, + ["--lsp", filePath], + { shell: true }, + ); + + const output = JSON.parse(command.stdout.toString()); + const compUnit: ast = output["ast"]; + + const name = filePath.replace(/^.*[\\/]/, ""); + + mainWindow.webContents.send("add-ast", { + compUnit, + name, + file: filePath, + unicode: getUnicode(filePath), + }); +} + +function setupMenu(mainWindow: BrowserWindow) { + let defaultMenu = Menu.getApplicationMenu(); + + if (!defaultMenu) return; + + const dialogConfig: OpenDialogOptions = { + title: "Select a file", + buttonLabel: "Upload", + properties: ["openFile"], + }; + + const selectFile = (_: Electron.MenuItem) => + dialog + .showOpenDialog(dialogConfig) + .then((result) => handleUpload(mainWindow, result.filePaths[0])); + + const exportFile = (_: Electron.MenuItem) => { + mainWindow.webContents.send("show-modal"); + }; + + const newMenu = new Menu(); + defaultMenu.items.forEach((x) => { + if (x.role?.toLowerCase() === "filemenu") { + const newSubmenu = new Menu(); + + if (!x.submenu) return; + + x.submenu.items.forEach((y) => newSubmenu.append(y)); + const fileItem = new MenuItem({ + label: "Upload File", + click: selectFile, + }); + newSubmenu.append(fileItem); + const exportItem = new MenuItem({ + label: "Export", + click: exportFile, + }); + newSubmenu.append(exportItem); + + newMenu.append( + new MenuItem({ + type: x.type, + label: x.label, + submenu: newSubmenu, + }), + ); + } else newMenu.append(x); + }); + + Menu.setApplicationMenu(newMenu); +} + +function substitute( + _: IpcMainInvokeEvent, + conclusion: string, + oldVar: string, + newVar: string, + file: string, +) { + const command = spawnSync( + "java", + [ + "-cp", + `${__dirname}/../../SASyLF.jar`, + "edu.cmu.cs.sasylf.VSCodeExtension", + `--substitute="${conclusion}"`, + `--old="${oldVar}"`, + `--new="${newVar}"`, + file, + ], + { shell: true }, + ); + + return JSON.parse(command.stdout.toString()); +} + +function parse( + _: IpcMainInvokeEvent, + conclusion: string, + rule: string, + file: string, +) { + const command = spawnSync( + "java", + [ + "-cp", + `${__dirname}/../../SASyLF.jar`, + "edu.cmu.cs.sasylf.VSCodeExtension", + `--parse="${conclusion}"`, + `--rule="${rule}"`, + file, + ], + { shell: true }, + ); + + return JSON.parse(command.stdout.toString()); +} + +function topdownParse( + _: IpcMainInvokeEvent, + premises: { premises: string[] }, + rule: string, + file: string, +) { + const command = spawnSync( + "java", + [ + "-cp", + `${__dirname}/../../SASyLF.jar`, + "edu.cmu.cs.sasylf.VSCodeExtension", + `--premises="${JSON.stringify(premises).replace(/"/g, '\\"')}"`, + `--rule="${rule}"`, + file, + ], + { shell: true }, + ); + + return JSON.parse(command.stdout.toString()); +} + +function addToClipboard(_: IpcMainInvokeEvent, content: string) { + clipboard.writeText(content); +} + +function saveFile(_: IpcMainInvokeEvent, theorem: string) { + dialog.showSaveDialog({}).then((result) => { + const filePath = result.filePath as PathOrFileDescriptor; + readFile(filePath, (err, data) => { + let content = ""; + if (err) { + if (err.code !== "ENOENT") { + throw err; + } + } else { + content += data.toString(); + } + writeFile(filePath, content + theorem, (err) => { + if (err) throw err; + }); + }); + }); +} + +const setup = (): void => { + const mainWindow = createWindow(); + setupMenu(mainWindow); + + ipcMain.handle("substitute", substitute); + ipcMain.handle("parse", parse); + ipcMain.handle("topdown-parse", topdownParse); + ipcMain.handle("add-to-clipboard", addToClipboard); + ipcMain.handle("save-file", saveFile); + + if (process.argv.length > 2) { + if (process.argv.length != 4) { + console.log("Incorrect command line arguments"); + return; + } + + mainWindow.webContents.on("did-finish-load", () => { + mainWindow.webContents.send("add-ast", { + compUnit: JSON.parse(process.argv[2]), + name: path.basename(process.argv[3]), + file: process.argv[3], + unicode: getUnicode(process.argv[3]), + }); + }); + } +}; + +// This method will be called when Electron has finished +// initialization and is ready to create browser windows. +app.on("ready", setup); + +app.on("window-all-closed", () => { + if (process.platform !== "darwin") app.quit(); +}); + +app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); +}); + +const reactDevToolsPath = path.join( + os.homedir(), + "/Library/Application Support/Arc/User Data/Default/Extensions/fmkadmapgofadopljbjfkapdkoienihi/5.0.0_0", +); + +app.whenReady().then(async () => { + await session.defaultSession.loadExtension(reactDevToolsPath); +}); diff --git a/lsp-sasylf/electron_app/src/preload.ts b/lsp-sasylf/electron_app/src/preload.ts new file mode 100644 index 00000000..158de261 --- /dev/null +++ b/lsp-sasylf/electron_app/src/preload.ts @@ -0,0 +1,30 @@ +import { contextBridge, ipcRenderer } from "electron"; +import { ast } from "./types"; + +contextBridge.exposeInMainWorld("electronAPI", { + substitute: (clause: string, oldVar: string, newVar: string, file: string) => + ipcRenderer.invoke("substitute", clause, oldVar, newVar, file), + parse: (conclusion: string, rule: string, file: string) => + ipcRenderer.invoke("parse", conclusion, rule, file), + addAST: ( + callback: (value: { + compUnit: ast; + name: string; + file: string; + unicode: string[]; + }) => void, + ) => + ipcRenderer.on("add-ast", (_event, value) => { + callback(value); + }), + addToClipboard: (content: string) => + ipcRenderer.invoke("add-to-clipboard", content), + saveFile: (theorem: string) => ipcRenderer.invoke("save-file", theorem), + showModal: (callback: () => void) => + ipcRenderer.on("show-modal", (_event) => callback()), + topdownParse: ( + premises: { premises: string[] }, + rule: string, + file: string, + ) => ipcRenderer.invoke("topdown-parse", premises, rule, file), +}); diff --git a/lsp-sasylf/electron_app/src/renderer.ts b/lsp-sasylf/electron_app/src/renderer.ts new file mode 100644 index 00000000..60038ae4 --- /dev/null +++ b/lsp-sasylf/electron_app/src/renderer.ts @@ -0,0 +1,2 @@ +import "./index.scss"; +import "./app"; diff --git a/lsp-sasylf/electron_app/src/styles/_app.scss b/lsp-sasylf/electron_app/src/styles/_app.scss new file mode 100644 index 00000000..30e9bb93 --- /dev/null +++ b/lsp-sasylf/electron_app/src/styles/_app.scss @@ -0,0 +1,7 @@ +.center-align { + align-items: center; +} + +.main-area { + min-width: 0; +} diff --git a/lsp-sasylf/electron_app/src/styles/_bank.scss b/lsp-sasylf/electron_app/src/styles/_bank.scss new file mode 100644 index 00000000..1d3651af --- /dev/null +++ b/lsp-sasylf/electron_app/src/styles/_bank.scss @@ -0,0 +1,54 @@ +.rule-like { + text-align: left; + text-wrap: nowrap; + width: stretch; +} + +.rule-likes { + display: flex; + flex-direction: column; + width: fit-content; +} + +.rule-like-text { + color: inherit; +} + +.exact, +#bank-canvas { + width: fit-content; +} + +.open-bank { + position: absolute; + top: 50%; + left: 40px; + border-color: rgba(0, 0, 0, 0); + z-index: 10; +} + +.bank-outer { + display: flex; + flex-direction: column; + overflow: hidden; + flex: none; + z-index: 1044; + background: white; + // transition: width 0.5s ease; +} + +.bank-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 5px; +} + +.bank-resizer { + flex: none; + width: 8px; + justify-self: flex-end; + cursor: col-resize; + resize: horizontal; +} diff --git a/lsp-sasylf/electron_app/src/styles/_canvas.scss b/lsp-sasylf/electron_app/src/styles/_canvas.scss new file mode 100644 index 00000000..7d502453 --- /dev/null +++ b/lsp-sasylf/electron_app/src/styles/_canvas.scss @@ -0,0 +1,32 @@ +.zoomable-canvas { + overflow: hidden; + position: relative; +} + +.canvas { + display: grid; + height: 90vh; + width: 97vw; + background-color: rgba(0, 0, 0, 0); + z-index: -1; +} + +.tools { + position: absolute; + z-index: 9; + display: flex; + justify-content: space-between; + bottom: 0; + right: 0; +} + +.input-theorem { + position: absolute; + top: 3.5vh; + right: 2vw; +} + +.theorem-list { + position: absolute; + z-index: 9; +} diff --git a/lsp-sasylf/electron_app/src/styles/_close.scss b/lsp-sasylf/electron_app/src/styles/_close.scss new file mode 100644 index 00000000..deee82fa --- /dev/null +++ b/lsp-sasylf/electron_app/src/styles/_close.scss @@ -0,0 +1,5 @@ +.close-button { + position: absolute; + top: 0; + right: 0; +} diff --git a/lsp-sasylf/electron_app/src/styles/_proof.scss b/lsp-sasylf/electron_app/src/styles/_proof.scss new file mode 100644 index 00000000..7ebffbd0 --- /dev/null +++ b/lsp-sasylf/electron_app/src/styles/_proof.scss @@ -0,0 +1,108 @@ +.proof-area { + justify-content: center; + justify-self: center; + position: absolute; + bottom: 0; +} + +.node-line { + background-color: black; + height: 4px; + border-radius: 5px; + width: stretch; +} + +.topdown-close { + position: relative; + bottom: 29px; +} + +.proof-node { + font-family: "Courier New", monospace; + align-items: flex-end; +} + +.drop-area { + display: flex; + align-items: center; + position: relative; + border: dashed; + border-radius: 10px; + bottom: 11px; + white-space: nowrap; +} + +.drop-node-area { + display: flex; + border: dashed; + border-radius: 10px; + width: stretch; + text-align: center; + white-space: nowrap; + align-items: center; + justify-content: center; +} + +.fill-width { + flex: 1; +} + +.no-wrap { + white-space: nowrap; +} + +.topdown-rule { + position: relative; + white-space: nowrap; + bottom: -3px; +} + +.topdown-node { + align-items: flex-end; + font-family: "Courier New", monospace; +} + +.topdown-close { + position: relative; + bottom: 29px; +} + +.drop-container { + white-space: nowrap; + align-self: stretch; + align-items: flex-end; +} + +.premises { + justify-content: center; +} + +.premise { + margin-right: 30px; +} + +.premise:last-child { + margin-right: 0; +} + +.centered-text { + text-align: center; +} + +.spacer { + visibility: hidden; +} + +.conclusion { + margin: auto; + align-items: center; + width: fit-content; +} + +.name-input { + width: 65px; +} + +.root-node { + margin: 0 20px 10px 20px; +} diff --git a/lsp-sasylf/electron_app/src/types.ts b/lsp-sasylf/electron_app/src/types.ts new file mode 100644 index 00000000..04262ca5 --- /dev/null +++ b/lsp-sasylf/electron_app/src/types.ts @@ -0,0 +1,111 @@ +export type ast = { + name: string; + theorems: theoremNode[]; + modules: moduleNode[]; + syntax: syntaxesNode; + judgments: judgmentNode[]; +}; + +export type theoremNode = { + name: string; + column: number; + line: number; + kind: string; + foralls: string[]; + conclusion: string; +}; + +export type moduleNode = { + name: string; + begin_column: number; + end_column: number; + begin_line: number; + end_line: number; + file: string; + ast: ast; + text?: string; +}; + +export type syntaxesNode = { + syntax_declarations: syntaxDeclarationNode[]; + sugars: syntaxSugarNode[]; +}; + +export type syntaxDeclarationNode = { + name: string; + column: number; + line: number; + clauses: clauseNode[]; +}; + +export type clauseNode = { + name: string; + column: number; + line: number; +}; + +export type syntaxSugarNode = { + name: string; + column: number; + line: number; +}; + +export type judgmentNode = { + name: string; + column: number; + line: number; + form: string; + rules: ruleNode[]; +}; + +export type ruleNode = { + premises: string[]; + name: string; + conclusion: string; + in_file: boolean; + column: number; + line: number; + file: string; +}; + +export type line = { + conclusion: string; + name: string; + rule: string; + premises: line[]; +}; + +export type input = { + free: boolean; + input: string[]; + type: string; + id: number; +}; + +export type tab = { + ast: ast; + id: number; + name: string; + inputs: input[]; + file: string; + unicode: string[]; +}; + +export type data = { + type: string; + text?: string; + ind?: string | number | undefined; +}; + +export type canvasState = { + x: number; + y: number; + scale: number; + id: number; +}; + +export enum Direction { + Both, + Up, + Down, +} diff --git a/lsp-sasylf/electron_app/tsconfig.json b/lsp-sasylf/electron_app/tsconfig.json new file mode 100644 index 00000000..60c6d5b1 --- /dev/null +++ b/lsp-sasylf/electron_app/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "ES6", + "allowJs": true, + "module": "commonjs", + "skipLibCheck": true, + "esModuleInterop": true, + "noImplicitAny": true, + "sourceMap": true, + "baseUrl": ".", + "outDir": "dist", + "moduleResolution": "node", + "resolveJsonModule": true, + "paths": { + "*": ["node_modules/*"] + }, + "jsx": "react-jsx" + }, + "include": ["src/**/*"] +} diff --git a/lsp-sasylf/electron_app/webpack.main.config.ts b/lsp-sasylf/electron_app/webpack.main.config.ts new file mode 100644 index 00000000..62f520fd --- /dev/null +++ b/lsp-sasylf/electron_app/webpack.main.config.ts @@ -0,0 +1,20 @@ +import type { Configuration } from 'webpack'; + +import { rules } from './webpack.rules'; +import { plugins } from './webpack.plugins'; + +export const mainConfig: Configuration = { + /** + * This is the main entry point for your application, it's the first file + * that runs in the main process. + */ + entry: './src/index.ts', + // Put your normal webpack config below here + module: { + rules, + }, + plugins, + resolve: { + extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'], + }, +}; diff --git a/lsp-sasylf/electron_app/webpack.plugins.ts b/lsp-sasylf/electron_app/webpack.plugins.ts new file mode 100644 index 00000000..600e7b9d --- /dev/null +++ b/lsp-sasylf/electron_app/webpack.plugins.ts @@ -0,0 +1,12 @@ +import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; +import HtmlWebpackPlugin from 'html-webpack-plugin'; + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); + +export const plugins = [ + new ForkTsCheckerWebpackPlugin({ + logger: 'webpack-infrastructure', + }), + new HtmlWebpackPlugin({ template: './src/index.html' }) +]; diff --git a/lsp-sasylf/electron_app/webpack.renderer.config.ts b/lsp-sasylf/electron_app/webpack.renderer.config.ts new file mode 100644 index 00000000..69c8b10d --- /dev/null +++ b/lsp-sasylf/electron_app/webpack.renderer.config.ts @@ -0,0 +1,19 @@ +import type { Configuration } from 'webpack'; + +import { rules } from './webpack.rules'; +import { plugins } from './webpack.plugins'; + +rules.push({ + test: /\.css$/, + use: [{ loader: 'style-loader' }, { loader: 'css-loader' }], +}); + +export const rendererConfig: Configuration = { + module: { + rules, + }, + plugins, + resolve: { + extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], + }, +}; diff --git a/lsp-sasylf/electron_app/webpack.rules.ts b/lsp-sasylf/electron_app/webpack.rules.ts new file mode 100644 index 00000000..6abdaab0 --- /dev/null +++ b/lsp-sasylf/electron_app/webpack.rules.ts @@ -0,0 +1,60 @@ +import type { ModuleOptions } from 'webpack'; +import autoprefixer from 'autoprefixer'; + +export const rules: Required['rules'] = [ + // Add support for native node modules + { + // We're specifying native_modules in the test because the asset relocator loader generates a + // "fake" .node file which is really a cjs file. + test: /native_modules[/\\].+\.node$/, + use: 'node-loader', + }, + { + test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, + parser: { amd: false }, + use: { + loader: '@vercel/webpack-asset-relocator-loader', + options: { + outputAssetBase: 'native_modules', + }, + }, + }, + { + test: /\.tsx?$/, + exclude: /(node_modules|\.webpack)/, + use: { + loader: 'ts-loader', + options: { + transpileOnly: true, + }, + }, + }, + { + test: /\.(scss)$/, + use: [ + { + // Adds CSS to the DOM by injecting a `