From 750445784236a89a8d70e1200c28dffa957cc329 Mon Sep 17 00:00:00 2001 From: Vincent Gramer Date: Thu, 9 Dec 2021 12:06:06 +0100 Subject: [PATCH] Update plugin to be compatible with 2021.3 (#119) * update gradle to version 7.2 * update to idea 213 the update of the gradle plugin comes with breaking changes see https://lp.jetbrains.com/gradle-intellij-plugin/ for more information * update kokin and idea sdk call to be compatible with 213 some of the change are not compatible with 211, so code is splitted into platform version code update kotlin code: * Type mismatch: value of a nullable type T is used where non-nullable type is expected. This warning will become an error soon. See https://youtrack.jetbrains.com/issue/KT-36770 for details * 'toLong(): Long' is deprecated. Conversion of Char to Number is deprecated. Use Char.code property instead. update idea skd call: * 'getVirtualFilesByName(Project!, String, GlobalSearchScope): (Mutable)Collection' is deprecated. Deprecated in Java Signed-off-by: Vincent Gramer --- .../release_and_pulbish_plugin_on_tag.yml | 2 +- .github/workflows/test.yml | 4 +- build.gradle.kts | 55 +++-- gradle-211.properties | 2 +- ...le-203.properties => gradle-212.properties | 14 +- gradle.properties | 9 +- gradle/wrapper/gradle-wrapper.jar | Bin 55190 -> 58694 bytes gradle/wrapper/gradle-wrapper.properties | 10 +- gradlew | 51 ++-- gradlew.bat | 21 +- .../ideaplugin/ide/actions/utils.kt | 0 .../ideaplugin/ide/runconfig/test/go/time.kt | 0 .../openpolicyagent/ideaplugin/OpaTestBase.kt | 36 +++ .../test/OpaTestRunConfigurationBase.kt | 2 +- .../ideaplugin/ide/actions/utils.kt | 96 ++++++++ .../ideaplugin/ide/runconfig/test/go/time.kt | 219 ++++++++++++++++++ .../openpolicyagent/ideaplugin/OpaTestBase.kt | 2 +- .../test/OpaTestRunConfigurationBase.kt | 169 ++++++++++++++ .../linemarkers/OpaCommandRunLineMarker.kt | 4 +- .../ideaplugin/openapiext/utils.kt | 6 +- .../ide/highlight/AnnotatorTestBase.kt | 3 +- .../ide/typing/RegoBraceMatcherTest.kt | 1 - .../ide/typing/RegoTypingTestBase.kt | 2 +- 23 files changed, 626 insertions(+), 82 deletions(-) rename gradle-203.properties => gradle-212.properties (51%) rename src/{ => 211}/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt (100%) rename src/{ => 211}/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt (100%) create mode 100644 src/211/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt rename src/{ => 211}/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt (98%) create mode 100644 src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt create mode 100644 src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt rename src/{ => 212}/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt (95%) create mode 100644 src/212/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt diff --git a/.github/workflows/release_and_pulbish_plugin_on_tag.yml b/.github/workflows/release_and_pulbish_plugin_on_tag.yml index 3ac0bb8..2cc6975 100644 --- a/.github/workflows/release_and_pulbish_plugin_on_tag.yml +++ b/.github/workflows/release_and_pulbish_plugin_on_tag.yml @@ -15,7 +15,7 @@ jobs: needs: [check-gradle-wrapper] strategy: matrix: - platform-version: [ 203, 211 ] + platform-version: [ 211, 212 ] env: ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }} ORG_GRADLE_PROJECT_publishChannel: stable diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 650204b..da41dec 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - platform-version: [ 203, 211 ] + platform-version: [ 211, 212 ] timeout-minutes: 60 env: ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }} @@ -70,7 +70,7 @@ jobs: strategy: fail-fast: false matrix: - platform-version: [ 203, 211 ] + platform-version: [ 211, 212 ] timeout-minutes: 60 env: ORG_GRADLE_PROJECT_platformVersion: ${{ matrix.platform-version }} diff --git a/build.gradle.kts b/build.gradle.kts index 518793b..c0e3619 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,13 +3,13 @@ * found in the LICENSE file. */ -import org.gradle.api.JavaVersion.VERSION_1_8 +import org.gradle.api.JavaVersion.VERSION_11 import org.gradle.api.internal.HasConvention import org.intellij.markdown.ast.getTextInNode import org.jetbrains.grammarkit.tasks.GenerateLexer import org.jetbrains.grammarkit.tasks.GenerateParser import org.jetbrains.intellij.tasks.RunIdeTask -import org.jetbrains.intellij.tasks.PublishTask +import org.jetbrains.intellij.tasks.PublishPluginTask import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -40,15 +40,15 @@ buildscript { idea { module { // https://github.com/gradle/kotlin-dsl/issues/537/ - excludeDirs = excludeDirs + file("testData") + file("deps") + excludeDirs = excludeDirs + file("testData") } } plugins { idea - kotlin("jvm") version "1.4.32" - id("org.jetbrains.intellij") version "0.7.3" - id("org.jetbrains.grammarkit") version "2021.1.2" + kotlin("jvm") version "1.6.0" + id("org.jetbrains.intellij") version "1.2.1" + id("org.jetbrains.grammarkit") version "2021.1.3" id("net.saliman.properties") version "1.5.1" } @@ -63,7 +63,6 @@ allprojects { repositories { mavenCentral() - jcenter() maven("https://cache-redirector.jetbrains.com/intellij-dependencies") } @@ -87,8 +86,8 @@ allprojects { } intellij { - version = baseVersion - sandboxDirectory = "$buildDir/$baseIDE-sandbox-$platformVersion" + version.set(baseVersion) + sandboxDir.set("$buildDir/$baseIDE-sandbox-$platformVersion") } sourceSets { @@ -103,25 +102,23 @@ allprojects { } } - configure { - sourceCompatibility = VERSION_1_8 - targetCompatibility = VERSION_1_8 + configure { + sourceCompatibility = VERSION_11 + targetCompatibility = VERSION_11 } tasks { withType { kotlinOptions { - jvmTarget = "1.8" - languageVersion = "1.3" - apiVersion = "1.3" + jvmTarget = "11" freeCompilerArgs = listOf("-Xjvm-default=enable") } } withType { - sinceBuild(prop("sinceBuild")) - untilBuild(prop("untilBuild")) - changeNotes(getLastReleaseNotes()) + sinceBuild.set(prop("sinceBuild")) + untilBuild.set(prop("untilBuild")) + changeNotes.set(provider {getLastReleaseNotes()}) // to check } withType { @@ -152,16 +149,16 @@ val pluginVersion = prop("pluginVersion") project(":plugin"){ version = "$pluginVersion$versionSuffix" intellij { - pluginName = "opa-idea-plugin" - val plugins = mutableListOf( + pluginName.set("opa-idea-plugin") + val pluginList = mutableListOf( "PsiViewer:$psiViewerPluginVersion" ) if (baseIDE == "idea") { - plugins += listOf( + pluginList += listOf( "java" ) } - setPlugins(*plugins.toTypedArray()) + plugins.set(pluginList) } dependencies{ @@ -178,12 +175,12 @@ project(":plugin"){ withType { jvmArgs("--add-exports", "java.base/jdk.internal.vm=ALL-UNNAMED") } - withType { - token(prop("publishToken")) - channels(channel) + withType { + token.set(prop("publishToken")) + channels.set(listOf(channel)) } runPluginVerifier { - ideVersions(prop("pluginVerifierIdeVersions")) + ideVersions.set(prop("pluginVerifierIdeVersions").split(',').map(String::trim).filter(String::isNotEmpty)) } buildSearchableOptions { // buildSearchableOptions task doesn't make sense for non-root subprojects @@ -225,8 +222,8 @@ project(":") { doLast { rootProject.allprojects .map { it.configurations } - .flatMap { listOf(it.compile, it.testCompile) } - .forEach { it.get().resolve() } + .flatMap { it.filter { c -> c.isCanBeResolved } } + .forEach { it.resolve() } } } } @@ -286,4 +283,4 @@ fun getLastReleaseNotes(changLogPath: String = "CHANGELOG.md"): String { releaseNotesChildren ) return org.intellij.markdown.html.HtmlGenerator(src, root, flavour).generateHtml() -} \ No newline at end of file +} diff --git a/gradle-211.properties b/gradle-211.properties index 4fc48cd..5cb86bf 100644 --- a/gradle-211.properties +++ b/gradle-211.properties @@ -10,7 +10,7 @@ psiViewerPluginVersion=211-SNAPSHOT # see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for more information sinceBuild=211.6693 -untilBuild=212.* +untilBuild=211.* # check the binary compatibility of the plugin against these idea versions ( more info at https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl) pluginVerifierIdeVersions= IC-2021.1 diff --git a/gradle-203.properties b/gradle-212.properties similarity index 51% rename from gradle-203.properties rename to gradle-212.properties index 3947deb..e68fc86 100644 --- a/gradle-203.properties +++ b/gradle-212.properties @@ -4,13 +4,13 @@ # # if you change the version of ide, also change psiViewerPluginVersion accordingly (cf https://plugins.jetbrains.com/plugin/227-psiviewer/versions) -ideaVersion=IC-2020.3.4 -pycharmCommunityVersion=PC-2020.3.4 -psiViewerPluginVersion=203-SNAPSHOT +ideaVersion=IC-2021.2.3 +pycharmCommunityVersion=PC-2021.2.3 +psiViewerPluginVersion=212-SNAPSHOT # see https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html for more information -sinceBuild=203.7148 -untilBuild=203.* +sinceBuild=212.4746 +untilBuild=213.* -# # check the binary compatibility of the plugin against these idea versions ( more info at https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl) -pluginVerifierIdeVersions= IC-2020.3.4 +# check the binary compatibility of the plugin against these idea versions ( more info at https://github.com/JetBrains/gradle-intellij-plugin#plugin-verifier-dsl) +pluginVerifierIdeVersions= IC-2021.2, IC-2021.3 diff --git a/gradle.properties b/gradle.properties index 0307a04..a617a68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,8 +9,8 @@ kotlin.code.style=official # gradle property file named gradle-${environmentName}.properties propertiesPluginEnvironmentNameProperty=platformVersion -# Supported platforms: 203, 211 -platformVersion=211 +# Supported platforms: 211, 212 +platformVersion=212 # Version of the ide used for the sandbox( possible value are 'idea' or 'pycharmCommunity') @@ -25,3 +25,8 @@ publishChannel=dev pluginVersion=nextVersion enableBuildSearchableOptions=false + +# Opt-out flag for bundling Kotlin standard library. +# See https://plugins.jetbrains.com/docs/intellij/kotlin.html#kotlin-standard-library for details. +# suppress inspection "UnusedProperty" +kotlin.stdlib.default.dependency = false \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 87b738cbd051603d91cc39de6cb000dd98fe6b02..490fda8577df6c95960ba7077c43220e5bb2c0d9 100644 GIT binary patch delta 25666 zcmZ6yV|1Pk(6*bTL1SBuZL4W)+h$|?YHZuKtvhLK+qUhb``O?7uC>><|IXi;HFKUb z$E4JL8yfpoM+c6WOfg^54h{x}`wa|?DKU-=D^VUC10ZK%G_f&qc8*pax10ZkAu!+O z%2E>aAm*9$%RtKNcQ0oclv?~RMtWse{}tH_lJqfLiGA1)41CTX^*i5Qs0ZrNVZZCS zus9PtPc61TPfZ(a17IEzU10ZZjbWYOSG(P@a>U3!>`+)aR)w3+{@Rtkj5~zKwJl;Lw$ChL{m?prE;f0{>NM90WzE5n|kWH zpH`>Li6)5%Ndo+9fI`V*J)tp7C&VQk7o+08!Ux-HVT!DtwQad%#91_kI`_d2o83T8 zD}KxuZPrE}wxy?wle0!L^qbJRdNB9)wGJCZ%f5YJ@9w4j$b*|$eECGIDZtI|AO1k< znE}sEmm&qSL$*6N_zdH$GTWKT9qep#!-kN_6`ggi#fg%bz5EXquhUK8La*ogsN{+P z>p|Uq-s;fxl^#Sda3%f0UVBBEZ;)_cVBf!kfq@BuS%K465<`K3k^dKB_}@Ge0P8B~ z8aV$5omcDV!&qp;1L}pPz|fhR)$7!wsb`=Vz-^n>7)ME}?A%Vx3ummatGoE0{ae3a zpD0#Xo99#AN`S=u2fj`zDPvI2#A@UA z@UF*hgp!%bjMT*uQcZP-yjhRp0L)=f6bhE9Zm*gnh*(%&u_o9-uMY-InU%DS#Dsv0 z!e=F1--lcX-biU>EVA$i5~GpSkRSMukb)pHXtOc)_H33Wr}R|ZfKnF~cX_rKv{5>Q zuPC)0gmtyegT~}J($?c0Mgk?v%EBpHH6SqhneGS65@bfc<%a{~S_Qtd+-WEHoVSxy{Vx|*lV)t81 zw|M>w8?uQX-i%b$Xhx@Y>gFdmZSxs&C-*6*H)_L(I?YWGRK^SQ%NaCE7bnj8jAObOyL2@ED%n9pKz>Co!c36epcoKSV2Gsa4U^5_DT*15XVCM z_orB64P59jYE2>)asM5a>`Wg=U@R|l=bAtXVf-8CNb zP!xmV^T5$pNxo?MBaW39<^}MrlD; z7Kj@&n>?Z%%&f9+=qNq54CltXAsu8 z-l?q-m^~)~ZqfNFOkDIWNP!;0T0X5Alj+)0^!&Hk(k&Mf1bN6FV0+Urlqp2nweva| zIZ-+OyNGoLrwxcAF3@_$7_hvU3Sd$Q^NB`~h2&ym7m(y{FjuiZ`&w2-{^hx&ck8ZP z|NG{jWzYWpy%$juup><(QyeIyBUoZTk^(y$fiJ|Q?@wfZ| zN-QGdG4Rl6iP$vhwT$z`k~s05UM%~^^iU%b^9G0{3H^EM%u_ija%K|R3`Kg#&-LSu zB8x!~t5yEsazvqs=n|gYCPPHsD8l~5!0pH<>Xzm$v(F5L*FzgKGbzzBHH%zeav(=B zhR-bzfHDvE<+AdEy3}Nnhxw#HECr%h5!<&xg90$HFrsGWMy@t4iRU;( zK(X4U3ceb~zfBuX_;F}uN$RAQKP?=P(iQ4WMb)ryy2yoHOY@R5POQ zA%*LN1Wzft!~4aN-5O6~#R;u+{tn6KTTlk{Yx>O%j^Zz3yU+;q5-(J?%SiHUK$gOl z@J{&;?-HCo5&iHs&O=)b-Lk1^I3Qt~x5#SBTkJ~KaXV$2!;Cwvkq#lde-Eyfw^u$M zw<=5KJg%P(QwXWlZQu1out3QQtvI6^ELdrB`^8<*n^q*XQN>>C{Dy1+nfNE{3nxqUmZWo#UY5SiXtqkL_TQZElme z*D*%#p>{XAmSE;0ApaM#sg4+%p~<*5n6pV|B@_u>VpgH*4H0By1OWvPggOosJRgaD zOT=O{8tZ9l0!f)|hH&=(EOsq>I1R7BU%5B#r$)6v0GaLD>bBegsg)iYni3Ui zeD_&rnBQS=Dy1E&RXk1v+N)!J!;H{$>oM=85v-w>+R`+y;l3^RT#la*XHabC4g0EHSS>?}Lp7KCsJpMK5_+ zL(wmGMk!X<<7!Lmh`+?#osqA?Wgny-yn{V@go*ozNgALNos@n6v(X}F9no~UC2nXu zqTU&kcw56^GuSv66@Onzo{D}3KcSdcRC0hIvJWP8!V*{)2wo9}n%D(@$uf4Cf+9P< zlheu7^L`IJ;Br|7WHh&x+_ur@kWcz!x&3o7Ix!q6n-JXsJ&>dZ?N9_v5zF;zd6y-; z3AOo`gsr{)S|(G0>G(6~Zwv$^=DR^(s7R7iu)dg-voVC-ks&B0 zDzMoV9{6&BZ`ff|P3Lr$AV9(&8AYfvWr(ee?3E4k5!-sAmOEs7VVB*fD9ZVH1k# zckyFHrkIl7#8t-Av>Ae{mcF85@7|&6#I^Z@w+X`VsLrRatT}4`KrXL86mT>CLHys6 zO&wZI5hu|IMi{V16T%Q^RK#kP8C2`lemBxNS57YW6ANUqVwRJgVK3)o@y+OiT(WT= zWtM!^cq2vR_yPlxZl_Yz=g=zN>)4#Hd5&}VxbDxNcc}ghd)OJ_!DHFbr)kQXjP~i# zm)0i?RcviF)`h%dp*hG^WqS0)c^!s!drihUz;_~yN&~9c2pW}s)LZUIPr)BO&)*`r z7t4kM^%#A9iJ%iJPFrbO84CNi%q2L)%0BQk;!W$eL-OO2|Fo_LFc({sL!;K#>SIu|O>hRSQF?JDJjQ1c2P4qg<-er^yZY2gTtupzw{b?R*_s}O zRh^tjG5|tkDj056b{W}k%YJcawj#%Hs*v;&s#x_5@RFr>=I+^{UnyVZ+$+NzC$9rw+-*|7(RdC*58N>p+do*) za!WncflMK#_oL}fq{7tfq{|y?}Yje{Ew5X z)`Im=U3UG7WoNs#dT3()W7sof!mMe@ffLAJV*?2R9YoG9DQXy%TSR4L$WAs55;7bf zaub=4E|J?9Y^rZ$cg)~K;=cW^6E4*eN2xbQp*P2_x9MWvQ6w|ReFj|9XT%Rh%-8<8 z>b%}?p7#6l&VA`~*Y`#20n|OmVqIT21mA97X{WmLLJTze?Qve@1bto@9`D2kAJ-}U zhGVw}uW?@zh_U>(V+DJN-);%MBoPhN`GKmC+LwALz<8$;`fDLJ`GrkTeP{af1?)?h z;a@S{*Vw?h9|#$!4yna`!GHYF5!{OXd%u5ui){TeOl$p4h5qjX7+$_*g8o_@`1@K9 z`|m3FO$AwXAO8heFlQLZcwrL!!rxX!>n^6=X1x&{i?OfVMda|2Ky)U6v_|1j#Zk5j z8^)+5CfqADGfp#pW1&_nd66sU9Jnh)&*dr(Q7BiVcePfZ&KqSFM3U)AI`jjP(#dLB zNd}Tuvzj&22z|Q-I8CDsmd4~tHmZth*bcBngiAK8$GlWH)0{GC@RLDuV>#<`R_tbt zG;$^EnRCX{l{B{2CY>biIKu1I4*XZ-&jS~#V^XkotLIUxr)iyBEQGNX7qi`_#{8#6 z%A2!E=W$9XKTbDZH?7?J!aHYC*X80gxn`WpN}n~S%X@2pFUhvnP@B!t%SJcviI~_{ zi49ZspWP~ojZ~-#?QR={O(s^uN!0;oQ~tj19d8O^eZ!rQXf=>{BxRT9*PGkhnBiG# z&VL%{)Vv_OHI03AaqC<7;tA}D71jz_N_}~)6y$hDpX|CMIiR<_FQh`#cWq{*N(#c; z^8Bph*{TnL15o;P(nTj1UTjAvWOlTSX5Gr;VA5nD#B|O8b#&Ev%usgD!$DmlzcunO z0VZf5j1a#mQRy*<%xfc@n$6{2nR0$$AR)z<>8e|e%MMR!V7Oraw5YL;-DQOiA5%qc zpjl#yprzDlE3J^HxvG7rYNKtx1^FS=S-T+o$#?cYRltGsbW+9|?W#03_J-@KY{#B6 zucp(H3N_xStIqt%y}Ri)r^jz zOtuKn6B6mo-D=`XcEfcY&N*-rOIp{!Qt!Wg6%)B)H_aq*$8Fe*BMjM)3siYZV~dM| zTVnYBbQBDZDY2-@!s7JvYV8=#f1|umTnTYPx->f?hJNDZr~y{*qFA~bD7u*{P&7gw z9!47|HsU+9*0=%!XcfTO2U{SO)tLgV>W8%y=lm<@b&3Z>G3JCGsIv*kC-C_-K z*Zs%|8}%&mP{JP%+0@TkVsv#)a(Kk@=flanV3We;AL%t`HPr(QW}&lR%KGllE@S{C z?$k*pOv`RjTKIaka8(vZcc&n=>sa!+QB(;I5*@)RS}hNlJs6YWAc zzl2koWn7K}+Z2RR$KE@LhzBfbO_Yg91iAHc#n~ZFvkONNdCj}Ltfm7J9tz+O39^py zxT8$Se{N+jq1hC2nE_WVuy6ujrO}Z?)*+9(V04Y*N2mb5LH*~z%t?_-Q=3ojWL`|j zhN$J3d`VW^oK$%!11LhvUn5y~EP+k9>aT~5JD|{J0a*SR>_aBx_G)4(QleLdGsz#& zqQY(1cM&*@x^aM?Buk!43I*O!xPqU~ZGyzLqDD^~v~Qb&-;9x1WNl@{R(JDVgp7)! z_3{@e5@scw^V+SA6x>P*S)y^et2|R=r8oM9Zwe@2F*|8I2NH_P0_84k3)eJtN<-vi zbfp^q9QDSP&OmI`A5_U6!!>$b+XfRhYcca@jNm2Bt007jWHlau_<=DTWP;6QuU3lK z(RG=ZJDt?jUMV3-rRnDE%&2BWiTy%bZ`)(SIR zg*MPK#OmAyS!*I@+5#5NKdcY8oOU!*eY3Mlal%z~uo^$JlH+U<3x9K!tZ|hHN8_vT zJ^q~(Q;}gDNes$nHALCcY17#RVXWM~F}WXjbHK>)6OZZfocO(pfN*~9iCqkW&FCe3 z6{3$0xICe?HC!lBb3goZ3suvI+Oy_Y;H|DSH;GHt*}Q)X(gRp!wZ}mJ;JbZ4Xoe>V z}jr96T%@(Me-4X_D%Fgd4Za;z-xjfC;Gh|PHv(>MjQ@VV>W!<|km_Q}2>y+@p7e#&flHxh z5-IUR{I#KW1t+bM`8U+7DCwiU_nL$&dyP%VYOLfC-p&&SnRa#S)O5itA16wPyNd0j zB)Yfm_CC#9+&Da`BaNjdJ|0$LH?sHDUvzA8>MhK(Jsqg-vvqPKzoa%*i>_y~id8tP z+*{BYdp4GS`PU-%y4=(>2H|08P7J>gQ;c;$>8_)3xvv5q!3L_fKYb^0Kd5+b(&2tk zbz$c%C%4u2@ZL_ZenfkLV>BPvyGzGRe$Q%)-X1IiK_riUz>Iw}d&<&mD%Bh-P{>)d zZyE9s+XpPrEi{>*lVDH6Sy>pL~&d@XHw& zcx~@3%>~8oUJ?)=uY-YZZnPE`=ozZn;wrT#(1DEl026rGracG?IW2Mi@Y09tAC*m{ zvuwo`!qZ6&($#yXSrNpAaF06Ua&l~Cmezi_$#T_9{qq5UG#~ z&;K?Z{4?^^?uu7f3&wQnJMz#L_?&x;w(~7j2LW=KaGbVtSL!FLO)XNBNv=f7ud~UG zHGqZ=eAV}t+Wa?YYYQX9Q3UIneDzi3a@+F7_iJ*Y|yH?gnUgjvK zBg-y0LJEeqGb1}Cc~pU!e4uf>TbjmxRr^+(XN}9Unl&Z#2k1!zVUhJp+MG~l#8&gV zFRdycSbT_9vpPqt%n*py>AYJnk;k!$$fuaI5WLSSt1>PB$wakj5H?oo2X=0I6u^;j zbWvvYMm}LahgV@B)QDUy)aYcU2F<`U(G@C_?7xJt7O;iS9JqzBremKMp2Vl$6B5&L zcOIgRyrlPcEYHL7TP>Sv?f&n#bh*P=<8Q-Vy4w;l-w?dhirwNUT*C&pCQ11wAfNLR zi&2MAAL`M`KKQ~4am>W2cx2cfumOjxd_jNrUGkq{p*Qf{gUD@0XD&BlQM)o1TTEmT zwR`N%d@sCK+dnQdi@N;hNyEZJ=NK(BtzVzM zf7TWVShRnAUuWx^`Um?#USTzh;TO?}=jBK!7;T|e8Np>BoOLK2)@TE(CJ9h5?pr9* zRKzv#flObtK&eVfgCANi$JA{@J-pv^5cn;2JilGD-S~YYW@ffz=G@EA;bCVSrO5a# z(ocZ+$Z$l)Iysx3fJxnf^Rh$mUg8_V-=JRUXBZXEL^2v>g*WR_pP zRo$laTxq!=O&Tf~+C%HfiJtlTnAhdX@gb{djras~Fftb@%q5i8M*xP{1yIkZsYgaW z8sPeFm^)_JzhAU4Wg#}1tVcj)Bwc&!W#+UR7<=vEvL6l8Ln_9qXZvFmA(y|Pcl#Oq z191u9Tr~XfLfKAEtzaK2o_`{5hthtNASHM6&<;NT#1-WQ2`S8s4}Rs39res2K#!oj zB0$$V9Ir1-<ZfBsBNWY{#8Oe+dc>v z7B1%WlzWY9tzgrE26Np|p4uM`-P}PdVt@P6Q=MA&HOP7HK}0&!h7ZD$6%u z{^;WFdOmyD59^n9uvJaQfH7_#YmHaQ<909*q&f9+44H9+d&D<(l;1 zSHs`nh#vMYY5P1vBi#4&?#?%zJYos9&WFVW8OzHC-VZ)LX@3HLkSj(EH3z>FDc>@S zWBN$;nqi(hiVfkDt}V?Skjh5 zV0l(wt?6VK*s}nrv!ypY7IhiKmLmEqjy|r8{P|1or^lV{p`R@yr-YFKps+tkRdJM zm|-_O{d6omjVeY)#8p=bWfRz$ayzRhkG`XLp<`zADO&(cB|7X7QEI15w0=#J+AlNs zTc?tlbJ|7@BG#xQYf)lfro|^>rcN#n0!q#$L=Ti`BWr|29~Q z8O$6yos$Q!e-kHPvZFUOlG-65NSYmciIMElI~mBO%=s;amDnbJM1o$BF`T4YTXUoF zYIxwfr*=+0KmXg1w$bR~#Py{|Ri8;aCF?5=Y$*~uWeNki$rF6c*g=>&^UQH>5ZS#rK%G+Iki3zy2ej(~s$Wehf zES&>b`mr=W0Rlcm!5(CekMDx&H|U75d!-Wvw;~JWrO>#dh=Tpom>La7EacFiBa*CQ-zf?bgs;i&2TH*&Wnc)Va7;4whQ}$Bjl=9Tmq;_ zmR@HZ$7O>GY@~)Cl29uQvMLOe;zBov<#CjB&{f2mF*FLptTM_l!NnG=jCtp(*tvf0 zWTg53jz}vH*D~g0Q-;K3kwAB*aLk!N7KW_#rY@d%r4Qd{z0)l`$)&cVL0^t#jDBlEL@*frQ~M+=@~~W0jw1eH;vp{g^gq=mBicTSuBD zy1j(-=tH?y(RtQjB`HY!G3HjEc@WXJ+NbHOUd{QfPq7^D9C@r(vH}%N6gOyFk%)_t zs?o?>ax~yxGB@C)tljECu$iWDbR^qR|G)^Mpfg=99_LFj#}#qZ+rqBGcwG*q!qj7$ zcpJ4X*03Hbm#=n}E1l&WX9v#3GEcj~uwrO(jU}tod~3?nEc!K1yBR~ziC*;`uf=@M zlJa6|(d?fdmo#6)Fh=z#oU!FvJtR0WuaIttSP96(OpYA8zpsXb1e)&5Dx4r}ToP>w9@G!hIkE9SNds~lG0CP!qeB-2 zoE6HPhTN`yXNp+h@6%xksf(4Z)+f5Vvt*Nt_PSM+4NkG2vg?Ts%2_t+XCr?aT~4`{ zQzY&mLf8e;PtFUv&;&i`FFN5r3COXz zBE8F0F|9Nt6tT2;A-3g_4m-t_X1NfGSFs<=b_7u#k`2Vo47BnpN~f`ByjasRgdVxu z#S==U?);S&oGkL%`864zm!N#3ab@jmaC)Q6DazusSt8O+(}CpP3xoinnhC2Xn9p$u zW)1lx4h=^9-Vbw*GR_!dA%i+6i~y6Jl3$iMT(QPR1~YDONLOq^$A;`?1=MHZ{xz$1LgohYJDOG7&kdn4q3tk}h-%8v%6o z&p$%cNv<%pF`>A__X~TF@@SV3kC9}(4&;P95R`vkpGL%aq^oG=Sb3c=cDsju^)us1 z)Fu6#NJu$~A0kwi8~kHiCIXW(<72fTiw%dB{O8_vfd!CGX6ml$XX zuj38mz^$6xX(2>5v^K>!e@{W$f2zcoC0L2;9eQWhLHm~JlUmC`*Yp#mZ z^3YmmTO&P-4%=2)yHzoK(|ZKX*F|a;x8#14CKc_K3P%PEKHivB-U!KXS8%R07!_j@ z7E3!nsgVCbMf+DEv}{J6+8ywmj^wFS2m~;m4skYs6rWY9{COst9o-onoMte!p;^L!End?_th{=p9nsuD2#2HscqxTv`ZV*FD%Sri}r&g2(l>q0x6TEk?s6~ z-rq~nwpya=JlQK(Q>=K!MtFAU{nzg<`&spB34wFsClxVYdvmVUO?n!Z-080=_s^~O z?&o*8sn6s5Q1Gfg!6Tz@^mYjwpg8=@Obo7=C;+R7f*>BXFD$5#)wIadDN3R>^oI1x zV1kZm_o#S-;P?m$7ZxNd&ZR#!nSJkot>0VNX$C^8HBQhN_dvXY-EEeA>p;<;ZrJ*< zAMJr&3u-5L-eZt`QP1hFCQt%(5wL&)GiOn*_7C01Jg~PIOya5~cLX*-8h!7=9>8~E z;QMYMiI1c>`p*e`Vk0Xmav#+p-VLUCSV79&eKdLLm!MyRsEe9Lno5f}Gl^WMA(VA_ z`*mKT(I@Ib$M{fV>69gSj`h6Ui`L| zvQ&!eMyt1y8|o$@Sh)3zrpk?EzWUo4fP(bLt}%yz$XP}6xz`-5zqCSne#WgNGHRNm z@poPkFY7^R|HI;@rc4?M4uq2RqRfs)v7MTX)$j1xiqG_d#uX~oC5D9emqhEMXy?L z4PVMcdu>OUn@T=k3(EBekwi2S;9en>D%Mv@o|SL|rp0IGCEaJ`Kkhu`Le}AYQ-@`? zdGGbmR?cgcxnP?u6=wlvCc$pIo^dLwJsMtB6C~gio7_^Eaa^QplaUtnt8p_5Pdy@U zp`1rVUAC_jlRu0FYTYq0KyIbV5T0wgKN_yZI5R-KiDtzgVRTHR*pQ$TfK0&uhue8T}{Fw6tB z-NQw}U$CR&q1xNZLbJ;U*ByBc3z?BnaY_C1)9;t~A=s~uO08XFiVw-&*z|L%m-3)o znin3w{2hM3@*NEi+1~vZpgCZ>?rWP=KFw+#VbaL0kJxjgAvQjb>V^;Yuf`3`hf;6i zOJacFK9i^}L|RapI-Tu6h1__#d}izUsI_#S^>BeaJ8Vz~2ZvcY1NmdE&(7c7Yq*@g zqMkXcYu8da2?a|P;Z+TnpzMo-!$t}^E2g2Sp5(>=@trpr*;(Bgps_e|%*}{baW-Aw z(w&`FHzoT%;kQywYKp!tO+Kgf?~47Snx2q^tCY#{Lm6mh?nBC0M*}!c^!8FaYZ=OBf7|?0&&yi~W^|p)?Vui$ERKa6 zoX-u%Sm$C|WL!f=02XWZisF+3Z7B^kpD7K+U6v!o^hWDUE!%z@Yj~blcAhP@2_4>) zV$VpE^w5hO6B>!a%8k-hw=byrNcp3gS9Xqgi%07vkmpneJV||75R$NRf(7)hrz1^~ z;-5ekMrw=+Gl~U;Z4I<-wXPQVR!6zrZ}}}Rwn=VQ`%|| zVRspGM3B*7shrMuiET!w)g@1;4M;LO(@m3_lLOQ-ViMf5`y+_@nP&T0^Pz!WdfX?h z#@nqiGR@*Ak?@=9Ze_*j_B5twL43%9YN}h3b>_B9XBIeD9eA`ei(#J2E2A1=msi3+ zqA~Be$)IH~pvCN&VkIH=MTW+c2MS-acj(_c?qx*5H`fGu4tZGhpL#_{XJXxCAi7c+ z{9Ovw`BZuMk)M!sv@bM}ihn8A=L1JBPvs7t9_XFCy|`w%3S#~EZhZFaN@u~wekcKY zdY?F=di^+KXL!RqmGJMwJGJmILnvkNFvBQS_@NJAfKtk?xpm+Ga@7FCg8D3;)GwG0 zF!zCs6QN!Oh&3{SZXHxZ0kb=;pto}(Q7!2EDd8((iFPg()-;3mKN$#EG4q|N>b|(T zoDq+YxT-M)=~$3VUigue4xSeMkXW&#qI!$v^P0k+n7BdVepb0uO?N*fztC^qIp;S7 z{mt~o0SOlVO4#vIS52n};7x?0kJA%q&r0)&dL$={kjD**KLEy=}mk=}Z0 zqXz_?_QvKhToYuUKU=G95A>$HKr&rdITN2Pqhi`{O9TUHaAoQDKL0Gj zpLY@hP(jGYA*SWO8KNsG>q7SGTc)$Kcq3gu`8$jWxMYRH0!c{{9$2p3SzNhl9RGzX z1uh0%a|D$(pX4LI`?BMcgN9+Z$%5|ogRc2tH>qH^OJO%-(YlGrCIqC){O~S~VePQa z0msOd5TtrSy}ci9k&sUbC3Z%I5*x7sdjwvCWGhrtg$Dfo;I|l0k-*-cwm&+0vcv(h zZ>dIHPbdKj%JPp1Zi1sV!}@qoQgeO}@wVXNu2}CJdyWtDH9cW%sZPHIZ;7y4#2FwVH7++*^w!f0ZnrVuMkgC-=$9cpy7|UCEa+0`*xt{Q&5so zyrHW@DcC>ZVZNfjD+!Tt3c~WL)+f&~UKJCP+TjH_aR)w@N}Jd2p_(c%Q-rLMr0NeF zwGxNyUQrkpB#I|DwSaFKa8*D3`Xc_nv5Xaj1acpU{}6jV_T8pZGV0R1B{w{2DY8Ytu&bkH26#4VKyAsb5^jrr~f-s zpjSC~c3EFG@t5ist9i9W^!lhh>uk8TA_e=bRRv{DF za|T-kCk5-{EzY*!y{np5T`UZqyV)vu*uUmf>T=jzska@E@PmM|7zx5Uxqm_}I zYlpP6TPpcYD*>~#>p%p{3So1dP$V^il7QV8%v~Osm=%jKbLVRuWj+QwV*7jZm84!Z zD)`DlQV|kwvo=$)pIDuL5+H35z)uA6Eb=&D*9h=jz91>4*OYx-S5&(9zo%`b9!qme(`8$c$QXDF4MOWvccf zbSc4l-I9(-!my7^^08A=Q@Q7%ORw^>XMhhW47h83;Xh1EbI7wWUf03umbT+o^&t*2 zn>7j87Cr(k(#L3~gg0FFA?f#uq228eagqukQHoIXl;qdrlCRKS zLSu5}?9^bKU_^&`&r)LvZCdkvGer&JJp2FE?SUJFLm>tj7$XB1*pL4e&@lfWPUNGy zxF|@~2r}}Z4-aCtL6ZDIXCwh(oQOh>13^KLxNs(Omk>39whdx|)oZH8Y_6`>w*F5x z>zxEtmyui2uB~b7q*~kluI}8#?(|*r^DCD7cif#mO78jn<^3i2eXH~NKal87@Hof& zIP_opH4ZAn?q3)yfazF9<;NuMoNRoF13^al9LDHz(~o2)$^BEJyp*^?UG|T zt<9+40P79Z1No~2O0=TvIcLW>H)rY|?*rUqbF(-T0%Ql$kxidb?iOwu+EsA* z(R*doGKu}IAFknQoo)8~T~pr0p0z{Q@XAq{MV^0A{C@3^vv>1Lrp)N~5huYCqnQa3 z6mbn8>dk?AH7q|+qiap)@b{$F!^F=!CRzl}*zB}j0VT3@_C(oy*T?hUEnIe;4tgT@ zE!Bw4>#ZC%b{!5flP!p{P|?`#ma!RX0x7ebw%TgZ)+lV>F2i#?*o~UlCf&$vG*gLE)jAM6E1^k; zvUrO?^Z!M5TCwy|8uUYGE1}6`0C!f?CN;ss;P+=+gC@bq`v{2lyoY~~=$h2CO zs^+(!ps%VL9XlsgS1&Wlc5QyCk!5K(P2%HDc5-b zm_``cuxTaH@fGS)q%n3a(Jca*QFg&>(Y3QL zg?PSN`>`x?T?^ATbRQsH0&JC=5e>64ld=aZwY_}%B5+%(zk`&>1#eF6#7L)EjJU`+=IP?0hll{$IP0ggg|B!;3|l`ASohP4-5S&tNpZ7)oaP!yJq&=t=BK2bb@ zkId1=WM)xCxdYysB0(+$%wD$>mxE$coAC)eKSj3u5=wcGZe|I}l^(t4r%9_$&JEZV z-X|WT{NS^A-fen~W6C*rE^cdecNKb)KT9*5R+YXud3Eo%RRWUU1fvFkr__M8urA_z zLxfY>;;;$}TjrX8G`)Q%&3j}ES0ZiXP|ek^1i}1UD)7Y?QFjCR5@T)6y^P)sGv_?$ zMo)}1ekB&88O4(9R?At^68Yt7#zv+ufU**_B{oN&Vm?}jF)j-CjL1b6d``Q{OVe-J z&aCbH7UgtahER)1b?gev*(B~ZD|@KrAl2&iCr+n~Uh@Z!mS$Srd!-`PHb|>$`L0Qh zAeP=*sYhaGw{)Zt+Nk4hg@?;Rbt{gzctsH{pSnMIXA|YYxO%1Q&z^ce)X8hiNVvSA z_-dB=;mw@lbtA#&sxQ(e)-0dWc4(K+CKoqW)2)!vuBibn&2WW+Eo-Qo62uZ=!z z)+4RI-i?i(c(Uzxb@GL4Lc*Sp5IK^meO$Vu1!ME32`6EV9sbn#@Bu={On0d+U#teD z_d;KAPO;v&1-BZmUwDAtv4~IT)b@_Ov~%fH=&N}4bpdudBd@|J{uBddAACe2eBUK3 z8*U8M1v9PsdKfU!FAhRB#cEj&DryYgeS6TyZ$aNhGQF!?jkM)i!`9C#KGzAOHPLLF zti~?XIqrNV%8qsTJN3;6W*&+PgS5*HO@_R$zLm;AI`mAL51Ab!Hm(%eV%rxe_OD?pa`$lViiEr~T47Vt?=!>(6>J zrTIf}XOzsOL#5SFe+%k)3h9px^!iRLtl`Deo`~Q}#(r^=5bU<4FZjMqbqMs!Zsv!R z#Abpp^%9Z}W&xzYkYWfpi3G)UwDaD>OI8XSO%#S~7=OlQT`#Y;e(3>2;&o|?L3DYR z7DWvBV=SP}Xu)D}chA(3KT)w?S#Kk%6nE!P}Exny~@e2WJ?i&;MB8QIBnqbGy z(8?`4BM6e(!+K@2@vn#&UHS=OD|zW(kFoItOYiUZa2VosaU=#yZ{d0Wjxbq$G)&dr zR06#J5`f@cqSBuWugf3!*}(W;5ven6)YAuxudXQoMdB;|UsC-|T7%s2GWp}D)&{?G z6FO&X*>TBX$?gV8{Xc^NLSXTf_(382-;eGSZJVvXsYBpHW1*4T^ zTxn+ShMkxqgnP!5aGM$Bj#k|JG8rbT4)Nq9=9b z)(w;lip%PGGToM2KIfcuX;qdt)G5~-RTJKFV@e;Co5mkh7_QC=q(0a-aoq>*LjVGU z02wM7`j*qOZRPMS?}2=oPd~3p@20u4Mb$1!dGj)ap&6T(VB~eJAe`LmHn5`hVKdpSY5nA^JIMQZpUs843V-gkGs=QB{Zl_HV@pyb!tqz%s60pYs3^NxCfapj$-z+M>bdEMqmxzhR(r%44CHVbDj-!ezR*%v zI*TNl?Rw`eLeGNRw}5*qO3-Yz5N~cBJ{}w%5mKb7JuvCIaB~^AWFl&_K)8aSYI9_I zSC9qCafjua@qQRoD5TS6QK(m9;0beCsP{LQbAV$cmcgf@jK2@%@$$AhJv zx;XBHZIn~vH6~8eBk*h0({xV&l}+9fLm1k|iwnjGFs~XXU8` z*Z%tfETM71GEI|c1j=#j?@zU(l6d$}(I2EpA1PVuz0k|=7BWW;g|1y)1iVBu3NgqL zr<)Hy*{#qsM&-<&XGB2<8r~vXezavzqx(_Tf^SqoAA#(}#q>Sj`$J$fu@)U$fiK`1ffd?xG*g4h?)Tc*7J6zuo zyAX4>ykm<~=e!IVwo@$7>#6$0lkt&lTHtiyX$mfXVVFPxTl0Pm=5Ubt|Qxh{2%XuVP|uY3Iu3xFrP(Py51KlI;VrPj-8Y z5h}ewzS^7n-h3+4_E&e|5h)kWYc-1F;U+@GN^}g=U&qF69?02JO4`3z za9CqJv8waQ&9zixkD;ZN&>v^y&x+O03hls}Vz=Ob0o}xP=fUAj8kE<6q~O64nP#w! zvQRwy!n+Yv-1a|zAXLvXYRen+mbKXdOC0Z~mCWpYZ~_K}wREesMItURrgjDQC-^&i zgJ-`_Gyk7LzA`M1rD+=$ceiB;?hb(vB)Cg(cY+3Y3yTN0#WlDS9D=*M6M_>Yc!CFc z_wb%`o+sar_v`&JRo!>hH9NcAGgZ@dyVBpFXs5*U!Z-RwU)iV^Od9}K$APQj7j)x> zpQq!yzzpQ;z>r(M@x_u8#dMCIq=%Q_xX@i}&pU}nF3+h&buV^SV}21eRJpcmHS5UY zyjzAOvcjJNoT>q1F3h1!(pX+6g&|(>VW{b`s`J;P^;Y=t=qw#-!xSBU)k!+`JpR_aJX?9 z=zRmKGFlfD*?aBf3-^=H8LZq3t1B!+s)u-RPk3t&`lJXqrykwbLDIMeAiev2ki|M5 z@UfJkfWX$xlosFWq3W2V6;h*R6;w|OS~qYM z6>gU{4wDu(Sb9rD7RnofTwQL}>b>Sqv(@EDq$ue_ULl11do?!e7nqpg1w4GG;=I2O zt8oJ!M+>s{GMt0HlyKKQefNP1{Jz=QFl6J3&;K6&Dw^EH$P=~efz#qq!mFOtaW#B( zA0MjboDy|U^BVnZ99Y;newR|8M(+H2@e?#M;2wZ`O%nF#l6tMv z_3+v}suc5MJ&<3qAVqyFVhC2uJDU!7XZV!)KqIyXJK2_kTJCP+BF1;@&;EX1tapMR{ynp_5G;!4ME>R`B^`EyahO<@`ONF z3_K)d%voaCpGpLnP+Lhva@h^~xhNNqq$6BWu+AVH2EZHo<&a)7?clYLVVpqUNmM8w zTwL8OF>@I-2d3>8?GydkU)y^WPevD*!T{&aNpG=*R3ejD9v2HgcUWv6+%p^Gin29i zL&C``G7MX*!Wz;xlZ1MUw67XonjQ4AJG?48?y9Rh_w8_A`wWS)LoUqEniRIK2?tUB zNZU*F9(q-UlU}xZ_V--MQmE_ zW(*umyo)z9nWIbMTGc66n_jHhi#2aae3*~8V%HF6pKu6UVOr;N_IW&gxoo0@%~)l9 z7cHYd2tDw$&l+Hnz+WtGj^?6KQ9NOmwv7-VT0a#H1KVTx1e7#@{h@K14WP_Xmdx}Xj?VE4XW0g!s{eM$8BXzg$-YHn z#o3wy9y60h`=W>AJ`aowZ>`$onPG{XHfCJTpxT?mmk|0~-86+?e8QHK>=U|q#8(~S zI(ftz6Yp&%J#1L8DD)(rihoq~G7bEUk+*Z5bOJN&nB>wUZB%Ys=N@v(vs5M}nK@vg zYc2RS^r6#=El~ypk>R|&d;66H2x2vtcDKv|83)d0!BkOcs=pbYJgIQ|*kuwu>~-Xn zb|l;|Y$!6Crk|yx?WZyJWzrotLAX3=eHzvG{;VC1{4{rV2XDj4n%g13jfNG`*do(R z@S}>s>(|9)BFSnnnVGe8FaiSRivB@NvwOO}g2vms6N1;~YhSZ+MxE%Zd+kegG0jP3vpXsBaJUFKSNVS46 zSY<`7R|H!qqaeESobc2Gf!u}wuK$%}9HA%!6GLE!-AbW-wCr+G1h~)`$M+2Gp25jo zudj@vV{9xfjoZcI`uO2Hfe7rVP60<2bPKVag~3>>wz1>;+9drEx?aaG-40$9+Z!UG zro~!Jvi)&uN%0C-#K|rXiVs!~nca*7V4iLc)#P|Sn|1YI{_UhVF2C1RKhH9mOP$yc zXtZkkIV@~wb&KB>W)1O~o?+(8l$Z|bv5K;;eAk{2YT0T~hWChgl_7>eFZkta@f`++ z+I!*9DGWaY8Y8vc%;|4;*Lrnx(w{%!1k5&9mgS+{chKsk?@PR>n}Qkc zjb5N|GKsyI589R!SI0vXC^mBX{@Gf)@U6o1r0!V*VuUYiSR01aUF|z06a(dtiheXt z=*jE*T_Xq_^Wn0^DTvq;iB0F%xW2${(Py#x*OmQZ-zl`}=&$u_3LC}yN4E^&o-)fza=p%_ce zNMuW-bJo$gTgzL|id`pHSs#dLwL}sY;5QYFHa#k`cCHCsVIt&tGQ?vzs8ZpPl7}v4`E5 z5*sBr;u~RZGgsVeynyZ#GO|1%r7}d_&S3Flj&k^TPKL7Hf1TP_$aU1Xk6pvsD{?-Dt57*omqt-&L2bVVTEuyy@w=y%@Xr`g7}|=WG9$ql>RwGh!B3 zf&N2d8sA$Im>9h|8)RU-&nx_^7{!}xC7ZMo6Y^2_vyD=HT)k~d{mNRcm_ia-Y)f|~ z^T&WMqAUttOv^Ee2GhMXsGCHwijA&9Mphs}Pz4{RO}n}+Fz%_l$POo|H%{(o;j3*o zbdppr@|X&7nWgq8S~9uys`K)ybMt&&I@}vB=dwGvUJ;9jFU+zfJix7C(r!>nkgfXiZevee? zdjL*oq%>x`nmB6564rZX=sGqd*{@3V|`&xlSy*TT51`S}fQM9xVVqKdCyz-ZMYtwXR7))?D!uS+8 zCp5Iur~T(Fng((Y`~Z_EiXs}_^nR#FBB3yPniLdOoDT266<^I0f?vE0Mo9y zUa1SHs-*; z*}VsD8p76xZdm>3x30N%Io=Yz$?#L|BY$^~LqPI*^DWn7)FQ&gm!py3wDh?Ml2y$} zg<}lORFNd>d3cbm3M>GTv=uGJ{=K=|Vve|4s2Y=mTW?I2TDKI>hu)fe!{I3ID?8_m z;LZ(2l9ikCsLq;|6-Rt(WgwZ3)p`yf1?0I=$^ zb4X^mDuEOa^N;;S*KNe}V>>?Xl-9D{Vx-pRU>X=qF7pA=Ax*SWQ&ZEcs}?f^plt<)@mudVAojW}_CI!-7|^@laA97cJsi z1dpGcz0S@8ir304vtQZ3RQIMT;2fvKg!$k-jqBxUc^@?2(c1Z1qCT(LPf^Qyzp-_4 zUrjIwt@*p7Zv^pSz>?=_kZ#pKo>jRe_|0+4V$Qy3PzFv;|L{vkNDn#(1~2N*$4)U! zqoY&%Q+uY;EUWc+Da?$)sJ-c;E*NgXh3ms}_*5A%qge0HkPDJTlF}~_2WL0N_TkQ& z;#w>GPIZ9L#_wjFOAs9Mz}KF?!{}bV(asByi@myr1ob10?$|qgUKa?9D$erTFtipsx663kh__2mgC1HksuY8gVLoOAV7uQEO zpg+mxoq3qOTCk)~5zBwZ0)FsFSN7hZ5LXq*Vx4XJ@lw(v;(|OM86HJ?09}@HrrBf&ef7Y}l3sBaUd)!5pbk<^MkdJ3tUaq-N(Jxmq~ z7YB@(a=OZ0W8TI{pR_~=u#kW<6%!g-n>g!%6so;EpaUVpexalzUT{p6loW&3;7Rk4 zG~t0!Dz@nO_LAfCUxRZvv*myLL!vN)4!hbwJCmQ;pa)ROfQ?!u$%Q?TC`30 zfnr;agr%I3#gu3S?CufIbc_li`o15+kyTo+b8}O06x4B##WlWZIh~@pL^Nj$Nrh)n zGV8d;uVb~+9%fhS9fEsl7t>{cDd+GyKaU@g3@!w>k-{9Vz4(VygYb*-mT8K2*Iatv z3F^pVB4t-mEmZr32_kxt&^X;B+f(IRM+@P4 zz&JSZX8adR=Lc;P9H3@bD%{UhPH3AqhgRh@RI(oPqfOLY?vkY2JOxadLY);<;jW}>}e z$e!Tp9yMHXQ0B}d(a52+abyr5ODVYT0GK!+J0n!LbT)ZsYtHK7Y~tu>?#$|F;%s7P z?h2Lcp+^_!{!9mGx2v_e3mnvpP-GGz{haL zAc3}V+foxLHYo?GV;B)*Z{=9w^U%Sova3Uq&Y_v(%<&3_(KHv8tU9A~8j3o}(SfFY z!`}My>MjW;jf>0o4rub3tTmC>m7L9Z-kK&>IDv&(mG!2AkXf&UdfLmVOd|+}#!;kL z)2C5Kryqr-#N1^n99kYTk}`rFj)GZ4$K-dXV61hEd}@@8zCQ9NaKo+Iitu2+?v4fM`S zNzPwh>f0RgK5&VOwl+T~gz1cI9{+)dE;UO;UAItA^t~)PW9TNhtVY+gR!Hf1B-EL! z$R?t@jtJv>hYMj=I(#F}_q5Dmh7d91mvTD%>f$CB%jPX+Ww*#0NhQ@_JOI;PrYYA& zqI;COxA<`CM@xB0(CA!~Qt(xy>gU%VomuSo8wgLu6hC~{tS6ti_Mrm5g=6Dwesbg7 ztoxJ~>9@~%acJq+X{A+P79{V=KJ_-<-KG9>{xT8zhhS!6)YJSEp<@H9;u@X(C6*t{ zpf=kVWHg5qWXs#QRWJ(?;5*D=;ws21(o3>-if=F9!V7m@qMV#r&b6LlWEVc3S_!}y zVZ|3w4qZ(*d%##fz^q*=r#5yxic8LBOKMJ8;ThZFRNA5(vxLCt)s(>7(Mzk-Iijsf zh*sFoj!43O#OWgIIiR$LTVjXj6Z8vM)}zUws_fd-yg1_Xj`cU?mZ7b>iD!;^1F&3R zvg&8oq;#e~X~jLSA%Al2Rb!op!hMJnRmL+6L zn=UHtaAa*xemPpM`+Zz2RQ~WQ$H)(kY;cDrHPIdVM=dvU^$39 zVjOj_Oc~rtCqCd7LrVE=rx1RBKu+vuUq_ ze9dDY^3)Q?x7HR=J{D85WI=~Z)^Sx|VH9*nAKkeLHks1%>DZs5)YF69O8;oGDJn}( zQkbs6s7p9ZRJavCtyUxsEHl8Fb6d)Hu>5i-kX72)nrGyx!$G!sE z2|;FmSfEMfX7ga&8^YqPpC1r1ZW6b-5om2W`|fYl&tP)6S#G^s98OXf@ap6(vW1Rh zx?e}3s2g$8V0l!VY5{pxU8{uWFDzbUzE6kNKD*Eu(6**!l4BB|p!N6dB`TJI+#(z}WkY`{cF^rYvsj}W)#{5{;}@Yo+yAXVV;eX;azPL(uW zOmScJnwsQ~OO@fWqJv9sU#HL)i@sQ=UL(Ke9iwbQBLW!E?Eqyq1gj=|>!`D%iCP|d zU`Zt8Wca{yLu^|o?3_(r_s+wC5mE-BN`zJ|CF;-Axz0+ZI%?s`g=~D}pz%4sgBL%X zwQhd*=G0$x`h&kbb(YAJ4KkU|31Fy<_EX`gv{@5Kh|D>2v?;dg)nm<6&?E>`#>o`9 za!M2HS%Q_A0P7e}dTM(VG4Tl~?fiF1a8aJy-k`tmf;W8a`AJ9M_UY7gB$qnPL zNUlq5NyBI73tw`3!m#6v$WhOnre5~ZcSyTpIWx+xI0jxMS^5w&NWPWp+)`&uWqTw2 zjwp!z3Y)c1x+#KBQwK;OoaKQ!oGm zuTxQEMIe&ur;1WAw98OM8EtJN$88@M&>6oS$0t@yO#!gX$R(7~%1dkgRs(l(7h=M@ z$FJnc^gHHtLzkugAm|sHk7ckXuBO)CR#3`O0 zf-wUEk82-z;6Z5%+j++eW?|*ynIxSg_7hwK0tGbROIFlNRuVF^p%2Gl7I3>+%mk92 zw1!2>nSwJ$|s8S=19c%my zSZ|9lzpxhwnj4cyG5PVzx-owxl77aB5&Mc3nS~#t^sV?3!8xcJCY+v%Gayc81f=ES zhBArQ@L7x<4n95dkRLWjgKR<7y0NzQeI0K_LxBPH{ERst+iKD^P*%-lmY35|@Vj^6 zQ`XGbJ9%Rd(mFls)X)_=$4`EthRi3W;H{Xnv~!FPfLu&w(S@vj2BCxQ=#~%VjhvEk zWo*n*3s(H|w7I&KgxWe_*y|Rg{aB78r{zNc^gs{IN-1>-7E#%im$-Mpb|E$sti@v&vyo@m3muqr6}?q z>a)%oS}S?w>7^D4O=-GnY+j4@-zo-W>r&29%bVo8%a>TIYgg4TyD2^Nk`>6NL2g5h zq0zzF%CB@9fx;pC7AT(?mx5Ff4EEKliQeP4#qi*+VK*0)G__(3=Ia0a6#`bk)a`By zbX@QvcRH?_kWfMReO?sYgx?nJf!`Lnii)`*gmCIY^R;D`aq$yywAOtNXLX@?{GQ^C z@)@Px6V99L9k)(>eBU0-C;S}Q8+{R0O+i%5&FZc~%OyMn-C~__NZ2mEKhJmxEAq>U zf+7T);>EHOjJ>jo*?GJ123s)jrXNlBlRdC<{1i0jXpNy(fyDEpGleLX=CHMh5FaB2 z0B35L%h)%j){^FmB&d@$_lv$09l3%QSFMEG)QV7jI?U>AVMY8=;-`2fk_o1&_1U5% zhvKd~>+4%?>vjTN!771?Epz5%RjiZL0|mzY;ZmvZ=Ey>gL)K}6Us}Qb+mVxHZ6+Ve z;$^W&2AgBeRT=IAuZ=W5h(DrL(~*`dxTIm%g~g9={*diU`$mU2O@&We; z6-dfF`=(1HiZh#LKmBqCr@O5wxnQfmb!F=&K%SM`hEQnXyrX}(Va#vmWs1q8@kTFv zkjCK;thG8q?wKB-j>}V&#txsLH@-EAl_sr3ql7hOAL)Cc9QrvlsQ=k#!?WTWvE-dlC1v?`yI*{lY7WYqDmxsT0N;WDZHc=^#0sL7eK^oT^R7l+gxHrKfW(Fo&<=dz_~Gncd47eHm&tw^S|`n!LmLZm z;fT-~R9>Ale6u(84oquNce~}H9x1NePn2!kZ;6j=c)W?4Xb6rpZ?c1;%9xX|rg>U*gh~m~T;*n6m+PZ!~$ozxzgrA05^4$?9DNT7=ZV zvElStDY-{n6mG@!&009!B!)@f#;5obttmgJu%|M|DW&!Hu?{XV*%Jzk$5(ybb(FWl z&v!re8DO1fj5i?$cD@i2A+#RQ78=dcL7$STA=UGUbqs|&I0O%DS3)nc+OnFXItS}I zGam?!Iz@-;D4XE0P#%_q^9Z2fFCK(-fbW09VP@L@gu`aG*8gWH45=9h)ddZZ{J&)T zrGtDBjv->uAJptnxBs+)0BSf$JT(E7ksk;t9ma-uA!4KdXD}N1c@RoM4B;Fi0{uZx z{0|CxEC_H!`3LylY{h?h1U*xXLQ4}ApuEN}|1d(v{M(2Ul0JkD`h!~X51=#l-#}zY z(Fh^rb_fWlB>%VT7ct@UscR1d0a+B!-76#%&@LuG0Xu1NV9lW&&DJGiPnFMND$TW7odMLHc(I0 ze|S4}hWK03{`DW^_$!6;Pb&y`>+=6g=s=3bc>t*{kkxSkz@Q5R(~29SIKc!^b%%;1 zko+DRsEQ34m;eIcd>~F6&B$2nRz|Y6#X85$KPM z&_AaGJr)Eog+M_<5bzWeU?L1EGC)K=U_dseut9%Bw}04lM?O1dpOyp=#XXB@(>$R6 zJmtc`2>pQ{N_Ylg%t!%#rTs5Hh7`g$gAK8a|L@uLGxK>cH6Hq{W|#oiInS!%tOx)p z|5>a|A%c!zLzZWO0Qkb^uCg>-2-6%B0H)+`6&(^X#}7CygLKas0>mpJl=CW}KbD|B zPVGZA)Qk$cB5?o~dmy?^3=q@>Ccw@JRHTCZ?}fyMxGn$z$PhQ0`-9N1$;PW)ZXptIHHx5+%Ysvbj6$HS}{QL9&-wT+sJOVUcfPvwKettmL LPARA%49x!ki(!$u delta 22506 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 MHTplY>HX9CKgzWmod5s; diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 859cc55..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,11 +1,5 @@ -# -# Use of this source code is governed by the MIT license that can be -# found in the LICENSE file. -# - -#Wed Mar 11 22:19:20 CET 2020 -distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index af6708f..2fe81a7 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m"' +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" @@ -109,8 +125,8 @@ if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` @@ -138,19 +154,19 @@ if $cygwin ; then else eval `echo args$i`="\"$arg\"" fi - i=$((i+1)) + i=`expr $i + 1` done case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi @@ -159,14 +175,9 @@ save () { for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done echo " " } -APP_ARGS=$(save "$@") +APP_ARGS=`save "$@"` # Collect all arguments for the java command, following the shell quoting and substitution rules eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" -# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong -if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then - cd "$(dirname "$0")" -fi - exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 0f8d593..9109989 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -13,8 +29,11 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt b/src/211/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt similarity index 100% rename from src/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt rename to src/211/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt b/src/211/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt similarity index 100% rename from src/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt rename to src/211/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt diff --git a/src/211/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt b/src/211/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt new file mode 100644 index 0000000..7611b21 --- /dev/null +++ b/src/211/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt @@ -0,0 +1,36 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin + +import com.intellij.testFramework.fixtures.BasePlatformTestCase +import org.openpolicyagent.ideaplugin.FileTree +import org.openpolicyagent.ideaplugin.OpaTestCase +import org.openpolicyagent.ideaplugin.TestProject + +abstract class OpaTestBase : BasePlatformTestCase(), OpaTestCase { + + open val dataPath: String = "" + + override fun getTestDataPath(): String = "${OpaTestCase.testResourcesPath}/${dataPath}" + + protected val fileName: String + get() = "$testName.rego" + + protected val testName: String + get() = camelOrWordsToSnake(getTestName(true)) + + companion object { + @JvmStatic + fun camelOrWordsToSnake(name: String): String { + if (' ' in name) return name.trim().replace(" ", "_") + + return name.split("(?=[A-Z])".toRegex()).joinToString("_", transform = String::toLowerCase) + } + } + + protected fun FileTree.create(): TestProject = + create(myFixture.project, myFixture.findFileInTempDir(".")) +} \ No newline at end of file diff --git a/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt b/src/211/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt similarity index 98% rename from src/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt rename to src/211/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt index 9c5b519..6819440 100644 --- a/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt +++ b/src/211/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt @@ -162,7 +162,7 @@ class ToStringPrinter : Printer { printable.printOn(this); } - override fun printHyperlink(text: String?, info: HyperlinkInfo?) {} + override fun printHyperlink(text: String, info: HyperlinkInfo?) {} override fun mark() {} diff --git a/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt b/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt new file mode 100644 index 0000000..25c5375 --- /dev/null +++ b/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/actions/utils.kt @@ -0,0 +1,96 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.ide.actions + + +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.editor.Document +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiManager +import com.intellij.psi.search.FilenameIndex +import com.intellij.psi.search.GlobalSearchScope +import org.openpolicyagent.ideaplugin.lang.RegoLanguage +import org.openpolicyagent.ideaplugin.lang.psi.RegoImport +import org.openpolicyagent.ideaplugin.lang.psi.RegoPackage +import org.openpolicyagent.ideaplugin.openapiext.virtualFile + +/** + * returns a nullable Pair containing the [Project] and the [Document] + * + * If the project is null or the file is not a Rego file then return null. + */ +fun getProjectAndDocument(e: AnActionEvent): Pair? { + val project = e.project ?: return null + val editor = e.getData(CommonDataKeys.EDITOR_EVEN_IF_INACTIVE) ?: getSelectedEditor(project) ?: return null + val document = editor.document + + return Pair(project, document) + +} + +fun getEditor(e: AnActionEvent): Editor? { + val project = e.project ?: return null + return e.getData(CommonDataKeys.EDITOR_EVEN_IF_INACTIVE) ?: getSelectedEditor(project) ?: return null +} + +fun getSelectedEditor(project: Project): Editor? = + FileEditorManager.getInstance(project).selectedTextEditor + +/** + * Returns package name in document as string + */ +fun getPackageAsString(document: Document, project: Project): String { + val file = document.virtualFile ?: return "" + val fileView = PsiManager.getInstance(project).findViewProvider(file) ?: return "" + val psiTree = fileView.getPsi(RegoLanguage) + val children = psiTree.children + for (child in children) { + if (child is RegoPackage) { + return child.ref.text + } + } + return "" +} + +/** + * Returns a list of the names of the imports in document as strings + */ +fun getImportsAsString(document: Document, project: Project): MutableList { + val file = document.virtualFile ?: return mutableListOf() + val fileView = PsiManager.getInstance(project).findViewProvider(file) ?: return mutableListOf() + val psiTree = fileView.getPsi(RegoLanguage) + val children = psiTree.children + var imports = mutableListOf() + for (child in children) { + if (child is RegoImport) { + var str = child.ref.text + val v = child.`var` + if (v != null) { + str += " as " + str += v.text + } + imports.add(str) + } + } + return imports +} + +/** + * Returns whether the project base directory has a file with name + * as direct child (rather than in subdirectory) + */ +fun fileDirectChildOfRoot(project: Project, name: String): Boolean { + val allfiles = FilenameIndex.getVirtualFilesByName(name, GlobalSearchScope.allScope(project)) + for (file in allfiles) { + if (file.parent.path == project.basePath) { + return true + } + } + return false +} diff --git a/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt b/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt new file mode 100644 index 0000000..59a9aff --- /dev/null +++ b/src/212/main/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/go/time.kt @@ -0,0 +1,219 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +// IMPORTANT NOTE: this file is a portage of time/format.go in kotlin + +// Original Copyright: +// +// Copyright 2010 The Go Authors. All rights reserved. +// license that can be found in the docs/devel/golang/golang-license.md file. +package org.openpolicyagent.ideaplugin.ide.runconfig.test.go + +private const val Nanosecond: Long = 1 +private const val Microsecond: Long = 1000 * Nanosecond +private const val Millisecond: Long = 1000 * Microsecond +private const val Second: Long = 1000 * Millisecond +private const val Minute: Long = 60 * Second +private const val Hour: Long = 60 * Minute + + +private val unitMap = mapOf( + "ns" to Nanosecond, + "us" to Microsecond, + "µs" to Microsecond, // U+00B5 = micro symbol + "μs" to Microsecond, // U+03BC = Greek letter mu + "ms" to Millisecond, + "s" to Second, + "m" to Minute, + "h" to Hour +) + + +/** + * A Duration represents the elapsed time between two instants + * as an [Long] nanosecond count. The representation limits the + * largest representable duration to approximately 290 years. + */ +class Duration(private val value: Long) { + fun toMilliseconds(): Long { + return value / 1000000 // 1e6 + } +} + +/** + * Throw when the conversion of a string to a Duration failed because the input string was not valid + */ +class DurationFormatException(msg: String) : NumberFormatException(msg) + + +/** + * ParseDuration parses a duration string. + * A duration string is a possibly signed sequence of + * decimal numbers, each with optional fraction and a unit suffix, + * such as "300ms", "-1.5h" or "2h45m". + * Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + * + * Throws [DurationFormatException] if the s is not a valid duration + */ +fun parseDuration(s1: String): Duration { + var s = s1 + // [-+]?([0-9]*(\.[0-9]*)?[a-z]+)+ + val orig = s + var d: Long = 0 + var neg = false + + // Consume [-+]? + if (s != "") { + val c = s[0] + if (c == '-' || c == '+') { + neg = c == '-' + s = s.substring(1) + } + } + // Special case: if all that is left is "0", this is zero. + if (s == "0") { + return Duration(0) + } + if (s == "") { + throw DurationFormatException("invalid duration ${orig}") + } + while (s != "") { + + var f: Long = 0 // integers before, after decimal point + var v: Long + var scale: Double = 1.0 // value = v + f/scale + + // The next character must be [0-9.] + if (!(s[0] == '.' || s[0] in '0'..'9')) { + throw DurationFormatException("invalid duration ${orig}") + } + // Consume [0-9]* + var pl = s.length + val (vt, st) = leadingInt(s) + v = vt + s = st + + val pre = pl != s.length // whether we consumed anything before a period + + // Consume (\.[0-9]*)? + var post = false + if (s != "" && s[0] == '.') { + s = s.substring(1) + pl = s.length + val (ft, scaleTemp, stt) = leadingFraction(s) + f = ft + scale = scaleTemp.toDouble() + s = stt + post = pl != s.length + } + if (!pre && !post) { + // no digits (e.g. ".s" or "-.s") + throw DurationFormatException("invalid duration $orig") + } + + // Consume unit. + + var i = 0 + while (i < s.length) { + val c = s[i] + if (c == '.' || c in '0'..'9') { + break + } + i++ + } + if (i == 0) { + throw DurationFormatException("missing unit in duration $orig") + } + val u = s.substring(0, i) + s = s.substring(i) + + + val unit = unitMap[u] ?: throw DurationFormatException("unknown unit $u in duration $orig") + if (v > Long.MAX_VALUE / unit) { + // overflow + throw DurationFormatException("invalid duration $orig") + } + v *= unit + if (f > 0) { + // float64 is needed to be nanosecond accurate for fractions of hours. + // v >= 0 && (f*unit/scale) <= 3.6e+12 (ns/h, h is the largest unit) + v += (f.toDouble() * (unit.toDouble() / scale)).toLong() + if (v < 0) { + // overflow + throw DurationFormatException("invalid duration $orig") + } + } + d += v + if (d < 0) { + // overflow + throw DurationFormatException("invalid duration $orig") + } + } + + if (neg) { + d = -d + } + return Duration(d) +} + +/** + * leadingInt consumes the leading [0-9]* from s. + */ +private fun leadingInt(s: String): Pair { + var i = 0 + var x: Long = 0 + while (i < s.length) { + val c = s[i] + if (c < '0' || c > '9') { + break + } + if (x > Long.MAX_VALUE / 10) { + // overflow + throw DurationFormatException("bad [0-9]*") + } + x = x * 10 + c.code.toLong() - '0'.code.toLong() + if (x < 0) { + // overflow + throw DurationFormatException("bad [0-9]*") + } + i++ + } + return Pair(x, s.substring(i)) +} + +/** + * leadingFraction consumes the leading [0-9]* from s. + * It is used only for fractions, so does not return an error on overflow, + * it just stops accumulating precision. + */ +private fun leadingFraction(s: String): Triple { + var i = 0 + var x: Long = 0 + var scale: Long = 1 + var overflow = false + while (i < s.length) { + val c = s[i] + if (c < '0' || c > '9') { + break + } + if (overflow) { + continue + } + if (x > Long.MAX_VALUE / 10) { + // It's possible for overflow to give a positive number, so take care. + overflow = true + continue + } + val y = x * 10 + c.code.toLong() - '0'.code.toLong() + if (y < 0) { + overflow = true + continue + } + x = y + scale *= 10 + i++ + } + return Triple(x, scale, s.substring(i)) +} diff --git a/src/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt b/src/212/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt similarity index 95% rename from src/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt rename to src/212/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt index bbd97c3..31ef991 100644 --- a/src/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt +++ b/src/212/test/kotlin/org/openpolicyagent/ideaplugin/OpaTestBase.kt @@ -24,7 +24,7 @@ abstract class OpaTestBase : BasePlatformTestCase(), OpaTestCase { fun camelOrWordsToSnake(name: String): String { if (' ' in name) return name.trim().replace(" ", "_") - return name.split("(?=[A-Z])".toRegex()).joinToString("_", transform = String::toLowerCase) + return name.split("(?=[A-Z])".toRegex()).joinToString("_", transform = String::lowercase) } } diff --git a/src/212/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt b/src/212/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt new file mode 100644 index 0000000..56480bb --- /dev/null +++ b/src/212/test/kotlin/org/openpolicyagent/ideaplugin/ide/runconfig/test/OpaTestRunConfigurationBase.kt @@ -0,0 +1,169 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.openpolicyagent.ideaplugin.ide.runconfig.test + +import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.execution.configurations.RunConfiguration +import com.intellij.execution.filters.HyperlinkInfo +import com.intellij.execution.testframework.Printable +import com.intellij.execution.testframework.Printer +import com.intellij.execution.testframework.sm.runner.SMTestProxy +import com.intellij.execution.testframework.sm.runner.ui.SMTRunnerConsoleView +import com.intellij.execution.ui.ConsoleViewContentType +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.util.ui.UIUtil +import org.assertj.core.api.Assertions.assertThat +import org.openpolicyagent.ideaplugin.OpaTestCase +import org.openpolicyagent.ideaplugin.ide.runconfig.OpaConfigurationFactory +import org.openpolicyagent.ideaplugin.ide.runconfig.RunConfigurationTestBase +import java.nio.file.Path +import java.nio.file.Paths + +abstract class OpaTestRunConfigurationBase : RunConfigurationTestBase() { + + fun createTestConfig( + bundleDir: Path? = null, + additionalArgs: String? = null, + env: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT + ): OpaTestRunConfiguration { + val runConfig = OpaConfigurationFactory(OpaTestRunConfigurationType()) + .createTemplateConfiguration(myFixture.project) as OpaTestRunConfiguration + + runConfig.bundleDir = bundleDir + runConfig.additionalArgs = additionalArgs + runConfig.env = env + return runConfig + } + + /** + * Run the test configuration and return the root of the testTree. + * + * see [checkTreeErrorMsg] and [getFormattedTestTree] for possible assertions + */ + protected fun executeAndGetTestRoot(configuration: RunConfiguration): SMTestProxy.SMRootTestProxy { + val result = execute(configuration) + val executionConsole = result.executionConsole as SMTRunnerConsoleView + val testsRootNode = executionConsole.resultsViewer.testsRootNode + with(result.processHandler) { + startNotify() + waitFor() + } + UIUtil.dispatchAllInvocationEvents() + Disposer.register(project, executionConsole) + return testsRootNode + } + + /** + * return the test tree as string for easy comparison with an expected output + * ( because it's a string, Intellij can generate a diff view if the test fails). + * + * Example: + * [root](-) + * .data.test.main.test_rule_2_should_be_ko(-) + * .data.test.main.test_should_raise_error(-) + * .data.test.main.test_rule1_should_be_ok(+) + * + * This method has been borrowed to IntelliJ rust project + */ + protected fun getFormattedTestTree(testTreeRoot: SMTestProxy.SMRootTestProxy) = + buildString { + if (testTreeRoot.wasTerminated()) { + append("Test terminated") + return@buildString + } + formatLevel(testTreeRoot) + } + + private fun StringBuilder.formatLevel(test: SMTestProxy, level: Int = 0) { + append(".".repeat(level)) + append(test.name) + when { + test.wasTerminated() -> append("[T]") + test.isPassed -> append("(+)") + test.isIgnored -> append("(~)") + else -> append("(-)") + } + + for (child in test.children) { + append('\n') + formatLevel(child, level + 1) + } + } + + /** + * check that the error message of each node matches the desired pattern + * the error message should correspond to the key "message" in [org.openpolicyagent.ideaplugin.ide.runconfig.test.OpaTestEventsConverter.fireFailedTest]) + * + * Desired patterns in file named "${node.name}.regex" are stored in the folder at "src/test/resources/${dataPath}/{testName}" + * + * eg. [org.openpolicyagent.ideaplugin.ide.runconfig.test.TestRunConfigurationExecutionOpaTest] + * + */ + protected fun checkTreeErrorMsg(root: SMTestProxy) { + val allNodes = mutableListOf(root) + allNodes.addAll(root.children) + + for (node in allNodes) { + val pattern = + FileUtil.loadFile( + Paths.get("${OpaTestCase.testResourcesPath}/${dataPath}/${testName}/${node.name}.regex").toFile() + ) + + val value = if (node == root) node.output else node.errorMessage ?: "" + + + assertThat(value) + .describedAs("test node '${node.name}' does not contain the expected error message") + .matches(Regex(pattern, RegexOption.MULTILINE).toPattern()) + } + + } + + /** + * name of the test currently being executed + */ + private val testName: String + get() = camelOrWordsToSnake(getTestName(true)) + + companion object { + @JvmStatic + fun camelOrWordsToSnake(name: String): String { + if (' ' in name) return name.trim().replace(" ", "_") + + return name.split("(?=[A-Z])".toRegex()).joinToString("_", transform = String::lowercase) + } + + private val SMTestProxy.output: String + get() { + val printer = ToStringPrinter() + printOn(printer) + return printer.output + } + } +} + +/** + * Fake printer that append text to a StringBuilder. It used to collect the output of the test root node. + */ +class ToStringPrinter : Printer { + private val buffer = StringBuilder() + + val output get() = buffer.toString() + + override fun print(text: String, contentType: ConsoleViewContentType) { + buffer.append(text) + } + + override fun onNewAvailable(printable: Printable) { + printable.printOn(this); + } + + override fun printHyperlink(text: String, info: HyperlinkInfo?) {} + + override fun mark() {} + +} \ No newline at end of file diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/linemarkers/OpaCommandRunLineMarker.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/linemarkers/OpaCommandRunLineMarker.kt index 1133f04..42b4b10 100644 --- a/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/linemarkers/OpaCommandRunLineMarker.kt +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/ide/linemarkers/OpaCommandRunLineMarker.kt @@ -61,12 +61,12 @@ class OpaCommandRunLineMarker : RunLineMarkerContributor() { if (element.elementType != RegoTypes.ASCII_LETTER) return null if (element.parent.parent.parent is RegoPackage) { - return Info(AllIcons.RunConfigurations.TestState.Run, { "eval or test package" }, ExecutorAction.getActions(1)) + return Info(AllIcons.RunConfigurations.TestState.Run, { "eval or test package" }, *ExecutorAction.getActions(1)) } if (element.parent.parent is RegoRuleHead) { val cmd = if(element.text.startsWith(REGO_TEST_RULE_PREFIX)) "test" else "eval" - return Info(AllIcons.RunConfigurations.TestState.Run, { "$cmd rule" }, ExecutorAction.getActions(1)) + return Info(AllIcons.RunConfigurations.TestState.Run, { "$cmd rule" }, *ExecutorAction.getActions(1)) } return null diff --git a/src/main/kotlin/org/openpolicyagent/ideaplugin/openapiext/utils.kt b/src/main/kotlin/org/openpolicyagent/ideaplugin/openapiext/utils.kt index f0399d5..e175749 100644 --- a/src/main/kotlin/org/openpolicyagent/ideaplugin/openapiext/utils.kt +++ b/src/main/kotlin/org/openpolicyagent/ideaplugin/openapiext/utils.kt @@ -130,7 +130,7 @@ val Document.isOPAPluginApplicable: Boolean val VirtualFile.document: Document? get() = FileDocumentManager.getInstance().getDocument(this) -inline fun getElements( +inline fun getElements( indexKey: StubIndexKey, key: Key, project: Project, scope: GlobalSearchScope? @@ -197,14 +197,14 @@ inline fun testAssert(action: () -> Boolean, lazyMessage: () -> Any) { fun runWithCheckCanceled(callable: () -> T): T = ApplicationUtil.runWithCheckCanceled(callable, ProgressManager.getInstance().progressIndicator) -fun Project.computeWithCancelableProgress(title: String, supplier: () -> T): T { +fun Project.computeWithCancelableProgress(title: String, supplier: () -> T): T { if (isUnitTestMode) { return supplier() } return ProgressManager.getInstance().runProcessWithProgressSynchronously(supplier, title, true, this) } -inline fun UserDataHolderEx.getOrPut(key: Key, defaultValue: () -> T): T = +inline fun UserDataHolderEx.getOrPut(key: Key, defaultValue: () -> T): T = getUserData(key) ?: putUserDataIfAbsent(key, defaultValue()) inline fun UserDataHolderEx.getOrPutSoft(key: Key>, defaultValue: () -> T): T = diff --git a/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/AnnotatorTestBase.kt b/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/AnnotatorTestBase.kt index 684346d..ba09e05 100644 --- a/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/AnnotatorTestBase.kt +++ b/src/test/kotlin/org/openpolicyagent/ideaplugin/ide/highlight/AnnotatorTestBase.kt @@ -23,8 +23,7 @@ abstract class AnnotatorTestBase(private val annotatorClass: KClass