mirror of
https://github.com/enso-org/enso.git
synced 2024-08-17 08:50:52 +03:00
In-memory suggestions (#9751)
This change replaces an sqllite-backed suggestions' repo with a simple, in-memory, one. As `completion` functionality has been implemented completely in GUI, there is no need to support it in backend, which simplifies a lot of functionality. Closes #9650 and #9471. # Important Notes Loading suggestions and sending them to GUI on startup is almost instantaneous. Previously it would take ~10s just for `Standard.Base`.
This commit is contained in:
parent
11dda5b9bc
commit
58009b7c04
23
build.sbt
23
build.sbt
@ -460,6 +460,7 @@ val scalaCompiler = Seq(
|
||||
"org.scala-lang" % "scala-reflect" % scalacVersion,
|
||||
"org.scala-lang" % "scala-compiler" % scalacVersion
|
||||
)
|
||||
val scalaCollectionCompatVersion = "2.8.1"
|
||||
|
||||
// === std-lib ================================================================
|
||||
|
||||
@ -503,7 +504,6 @@ val scalameterVersion = "0.19"
|
||||
val scalatestVersion = "3.3.0-SNAP4"
|
||||
val shapelessVersion = "2.3.10"
|
||||
val slf4jVersion = JPMSUtils.slf4jVersion
|
||||
val slickVersion = "3.4.1"
|
||||
val sqliteVersion = "3.42.0.0"
|
||||
val tikaVersion = "2.4.1"
|
||||
val typesafeConfigVersion = "1.4.2"
|
||||
@ -1104,9 +1104,7 @@ lazy val searcher = project
|
||||
.settings(
|
||||
frgaalJavaCompilerSetting,
|
||||
libraryDependencies ++= jmh ++ Seq(
|
||||
"com.typesafe.slick" %% "slick" % slickVersion,
|
||||
"org.xerial" % "sqlite-jdbc" % sqliteVersion,
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
||||
) ++ logbackTest
|
||||
)
|
||||
.configs(Benchmark)
|
||||
@ -2169,14 +2167,15 @@ lazy val `engine-runner` = project
|
||||
commands += WithDebugCommand.withDebug,
|
||||
inConfig(Compile)(truffleRunOptionsSettings),
|
||||
libraryDependencies ++= Seq(
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % Provided,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Provided,
|
||||
"commons-cli" % "commons-cli" % commonsCliVersion,
|
||||
"com.monovore" %% "decline" % declineVersion,
|
||||
"org.jline" % "jline" % jlineVersion,
|
||||
"org.typelevel" %% "cats-core" % catsVersion,
|
||||
"junit" % "junit" % junitVersion % Test,
|
||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test
|
||||
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % Provided,
|
||||
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Provided,
|
||||
"commons-cli" % "commons-cli" % commonsCliVersion,
|
||||
"com.monovore" %% "decline" % declineVersion,
|
||||
"org.jline" % "jline" % jlineVersion,
|
||||
"org.typelevel" %% "cats-core" % catsVersion,
|
||||
"junit" % "junit" % junitVersion % Test,
|
||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
||||
"org.scala-lang.modules" %% "scala-collection-compat" % scalaCollectionCompatVersion
|
||||
),
|
||||
run / connectInput := true
|
||||
)
|
||||
|
@ -156,11 +156,6 @@ The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.scala-logging.scala-logging_2.13-3.9.4`.
|
||||
|
||||
|
||||
'slick_2.13', licensed under the Two-clause BSD-style license, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.slick.slick_2.13-3.4.1`.
|
||||
|
||||
|
||||
'ssl-config-core_2.13', licensed under the Apache-2.0, is distributed with the engine.
|
||||
The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `com.typesafe.ssl-config-core_2.13-0.4.3`.
|
||||
@ -421,9 +416,9 @@ The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.netbeans.api.org-openide-util-lookup-RELEASE180`.
|
||||
|
||||
|
||||
'reactive-streams', licensed under the MIT-0, is distributed with the engine.
|
||||
The license file can be found at `licenses/MIT-0`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.reactivestreams.reactive-streams-1.0.4`.
|
||||
'reactive-streams', licensed under the CC0, is distributed with the engine.
|
||||
The license file can be found at `licenses/CC0`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.reactivestreams.reactive-streams-1.0.3`.
|
||||
|
||||
|
||||
'scala-collection-compat_2.13', licensed under the Apache-2.0, is distributed with the engine.
|
||||
@ -471,11 +466,6 @@ The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `org.typelevel.jawn-parser_2.13-1.4.0`.
|
||||
|
||||
|
||||
'sqlite-jdbc', licensed under the The Apache Software License, Version 2.0, is distributed with the engine.
|
||||
The license information can be found along with the copyright notices.
|
||||
Copyright notices related to this dependency can be found in the directory `org.xerial.sqlite-jdbc-3.42.0.0`.
|
||||
|
||||
|
||||
'snakeyaml', licensed under the Apache License, Version 2.0, is distributed with the engine.
|
||||
The license file can be found at `licenses/APACHE2.0`.
|
||||
Copyright notices related to this dependency can be found in the directory `org.yaml.snakeyaml-1.33`.
|
||||
|
@ -1,25 +0,0 @@
|
||||
Copyright 2011-2021 Lightbend, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
121
distribution/engine/THIRD-PARTY/licenses/CC0
Normal file
121
distribution/engine/THIRD-PARTY/licenses/CC0
Normal file
@ -0,0 +1,121 @@
|
||||
Creative Commons Legal Code
|
||||
|
||||
CC0 1.0 Universal
|
||||
|
||||
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||
HEREUNDER.
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for
|
||||
the purpose of contributing to a commons of creative, cultural and
|
||||
scientific works ("Commons") that the public can reliably and without fear
|
||||
of later claims of infringement build upon, modify, incorporate in other
|
||||
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||
and for any purposes, including without limitation commercial purposes.
|
||||
These owners may contribute to the Commons to promote the ideal of a free
|
||||
culture and the further production of creative, cultural and scientific
|
||||
works, or to gain reputation or greater distribution for their Work in
|
||||
part through the use and efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any
|
||||
expectation of additional consideration or compensation, the person
|
||||
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not
|
||||
limited to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display,
|
||||
communicate, and translate a Work;
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
iii. publicity and privacy rights pertaining to a person's image or
|
||||
likeness depicted in a Work;
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||
in a Work;
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation
|
||||
thereof, including any amended or successor version of such
|
||||
directive); and
|
||||
vii. other similar, equivalent or corresponding rights throughout the
|
||||
world based on applicable law or treaty, and any national
|
||||
implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||
of action, whether now known or unknown (including existing as well as
|
||||
future claims and causes of action), in the Work (i) in all territories
|
||||
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||
treaty (including future time extensions), (iii) in any current or future
|
||||
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||
including without limitation commercial, advertising or promotional
|
||||
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||
member of the public at large and to the detriment of Affirmer's heirs and
|
||||
successors, fully intending that such Waiver shall not be subject to
|
||||
revocation, rescission, cancellation, termination, or any other legal or
|
||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||
as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||
be judged legally invalid or ineffective under applicable law, then the
|
||||
Waiver shall be preserved to the maximum extent permitted taking into
|
||||
account Affirmer's express Statement of Purpose. In addition, to the
|
||||
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||
maximum duration provided by applicable law or treaty (including future
|
||||
time extensions), (iii) in any current or future medium and for any number
|
||||
of copies, and (iv) for any purpose whatsoever, including without
|
||||
limitation commercial, advertising or promotional purposes (the
|
||||
"License"). The License shall be deemed effective as of the date CC0 was
|
||||
applied by Affirmer to the Work. Should any part of the License for any
|
||||
reason be judged legally invalid or ineffective under applicable law, such
|
||||
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||
of the License, and in such case Affirmer hereby affirms that he or she
|
||||
will not (i) exercise any of his or her remaining Copyright and Related
|
||||
Rights in the Work or (ii) assert any associated claims and causes of
|
||||
action with respect to the Work, in either case contrary to Affirmer's
|
||||
express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
b. Affirmer offers the Work as-is and makes no representations or
|
||||
warranties of any kind concerning the Work, express, implied,
|
||||
statutory or otherwise, including without limitation warranties of
|
||||
title, merchantability, fitness for a particular purpose, non
|
||||
infringement, or the absence of latent or other defects, accuracy, or
|
||||
the present or absence of errors, whether or not discoverable, all to
|
||||
the greatest extent permissible under applicable law.
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without
|
||||
limitation any person's Copyright and Related Rights in the Work.
|
||||
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||
consents, permissions or other rights required for any use of the
|
||||
Work.
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to
|
||||
this CC0 or use of the Work.
|
@ -1,16 +0,0 @@
|
||||
MIT No Attribution
|
||||
|
||||
Copyright <YEAR> <COPYRIGHT HOLDER>
|
||||
|
||||
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.
|
||||
|
||||
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.
|
@ -0,0 +1,10 @@
|
||||
/************************************************************************
|
||||
* Licensed under Public Domain (CC0) *
|
||||
* *
|
||||
* To the extent possible under law, the person who associated CC0 with *
|
||||
* this code has waived all copyright and related or neighboring *
|
||||
* rights to this code. *
|
||||
* *
|
||||
* You should have received a copy of the CC0 legalcode along with this *
|
||||
* work. If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.*
|
||||
************************************************************************/
|
@ -1,2 +0,0 @@
|
||||
See https://github.com/reactive-streams/reactive-streams-jvm for more information.
|
||||
|
@ -1,202 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
@ -1,24 +0,0 @@
|
||||
Copyright (c) 2006, David Crawshaw. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
@ -1,7 +0,0 @@
|
||||
Copyright (c) 2007 David Crawshaw <david@zentus.com>
|
||||
|
||||
Copyright (c) 2021 Gauthier Roebroeck <gauthier.roebroeck@gmail.com>
|
||||
|
||||
Copyright 2007 Taro L. Saito
|
||||
|
||||
Copyright 2016 Magnus Reftel
|
@ -158,7 +158,6 @@ transport formats, please look [here](./protocol-architecture).
|
||||
- [`search/getSuggestionsDatabaseVersion`](#searchgetsuggestionsdatabaseversion)
|
||||
- [`search/suggestionsDatabaseUpdate`](#searchsuggestionsdatabaseupdate)
|
||||
- [`search/suggestionsOrderDatabaseUpdate`](#searchsuggestionsorderdatabaseupdate)
|
||||
- [`search/completion`](#searchcompletion)
|
||||
- [Input/Output Operations](#inputoutput-operations)
|
||||
- [`io/redirectStandardOutput`](#ioredirectstandardoutput)
|
||||
- [`io/suppressStandardOutput`](#iosuppressstandardoutput)
|
||||
|
@ -10,8 +10,7 @@ import org.apache.commons.io.FileUtils;
|
||||
import org.enso.languageserver.data.ProjectDirectoriesConfig;
|
||||
import org.enso.languageserver.event.InitializedEvent;
|
||||
import org.enso.logger.masking.MaskedPath;
|
||||
import org.enso.searcher.sql.SqlDatabase;
|
||||
import org.enso.searcher.sql.SqlSuggestionsRepo;
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import scala.jdk.javaapi.FutureConverters;
|
||||
@ -26,8 +25,7 @@ public class RepoInitialization implements InitializationComponent {
|
||||
|
||||
private final ProjectDirectoriesConfig projectDirectoriesConfig;
|
||||
private final EventStream eventStream;
|
||||
private final SqlDatabase sqlDatabase;
|
||||
private final SqlSuggestionsRepo sqlSuggestionsRepo;
|
||||
private final InMemorySuggestionsRepo suggestionsRepo;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
@ -41,20 +39,18 @@ public class RepoInitialization implements InitializationComponent {
|
||||
* @param executor the executor that runs the initialization
|
||||
* @param projectDirectoriesConfig configuration of language server directories
|
||||
* @param eventStream the events stream
|
||||
* @param sqlDatabase the sql database
|
||||
* @param sqlSuggestionsRepo the suggestions repo
|
||||
* @param suggestionsRepo the suggestions repo
|
||||
*/
|
||||
public RepoInitialization(
|
||||
Executor executor,
|
||||
ProjectDirectoriesConfig projectDirectoriesConfig,
|
||||
EventStream eventStream,
|
||||
SqlDatabase sqlDatabase,
|
||||
SqlSuggestionsRepo sqlSuggestionsRepo) {
|
||||
InMemorySuggestionsRepo
|
||||
suggestionsRepo) { // Java won't allow Future type constructor in SuggestionsRepo[Future]
|
||||
this.executor = executor;
|
||||
this.projectDirectoriesConfig = projectDirectoriesConfig;
|
||||
this.eventStream = eventStream;
|
||||
this.sqlDatabase = sqlDatabase;
|
||||
this.sqlSuggestionsRepo = sqlSuggestionsRepo;
|
||||
this.suggestionsRepo = suggestionsRepo;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,8 +60,7 @@ public class RepoInitialization implements InitializationComponent {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> init() {
|
||||
return initSqlDatabase()
|
||||
.thenComposeAsync(v -> initSuggestionsRepo(), executor)
|
||||
return initSuggestionsRepo()
|
||||
.whenCompleteAsync(
|
||||
(res, err) -> {
|
||||
if (err == null && res != null) {
|
||||
@ -76,33 +71,9 @@ public class RepoInitialization implements InitializationComponent {
|
||||
executor);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> initSqlDatabase() {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> {
|
||||
try {
|
||||
lock.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
if (!isInitialized) {
|
||||
logger.info("Initializing sql database [{}]...", sqlDatabase);
|
||||
sqlDatabase.open();
|
||||
logger.info("Initialized sql database [{}].", sqlDatabase);
|
||||
}
|
||||
},
|
||||
executor)
|
||||
.whenCompleteAsync(
|
||||
(res, err) -> {
|
||||
if (err != null) {
|
||||
logger.error("Failed to initialize sql database [{}].", sqlDatabase, err);
|
||||
}
|
||||
},
|
||||
executor);
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> initSuggestionsRepo() {
|
||||
return CompletableFuture.runAsync(
|
||||
() -> logger.info("Initializing suggestions repo [{}]...", sqlDatabase), executor)
|
||||
() -> logger.info("Initializing suggestions repo [{}]...", suggestionsRepo), executor)
|
||||
.thenComposeAsync(
|
||||
v -> {
|
||||
if (!isInitialized)
|
||||
@ -112,11 +83,12 @@ public class RepoInitialization implements InitializationComponent {
|
||||
},
|
||||
executor)
|
||||
.thenRunAsync(
|
||||
() -> logger.info("Initialized Suggestions repo [{}].", sqlDatabase), executor)
|
||||
() -> logger.info("Initialized Suggestions repo [{}].", suggestionsRepo), executor)
|
||||
.whenCompleteAsync(
|
||||
(res, err) -> {
|
||||
if (err != null) {
|
||||
logger.error("Failed to initialize SQL suggestions repo [{}].", sqlDatabase, err);
|
||||
logger.error(
|
||||
"Failed to initialize SQL suggestions repo [{}].", suggestionsRepo, err);
|
||||
} else {
|
||||
eventStream.publish(InitializedEvent.SuggestionsRepoInitialized$.MODULE$);
|
||||
}
|
||||
@ -127,12 +99,9 @@ public class RepoInitialization implements InitializationComponent {
|
||||
return CompletableFuture.runAsync(
|
||||
() ->
|
||||
logger.warn(
|
||||
"Failed to initialize the suggestions database [{}].", sqlDatabase, error),
|
||||
"Failed to initialize the suggestions database [{}].", suggestionsRepo, error),
|
||||
executor)
|
||||
.thenRunAsync(sqlDatabase::close, executor)
|
||||
.thenComposeAsync(v -> clearDatabaseFile(0), executor)
|
||||
.thenRunAsync(sqlDatabase::open, executor)
|
||||
.thenRunAsync(() -> logger.info("Retrying database initialization."), executor)
|
||||
.thenRunAsync(() -> logger.info("Retrying suggestions repo initialization."), executor)
|
||||
.thenComposeAsync(v -> doInitSuggestionsRepo(), executor);
|
||||
}
|
||||
|
||||
@ -191,6 +160,6 @@ public class RepoInitialization implements InitializationComponent {
|
||||
}
|
||||
|
||||
private CompletionStage<Void> doInitSuggestionsRepo() {
|
||||
return FutureConverters.asJava(sqlSuggestionsRepo.init()).thenAcceptAsync(res -> {}, executor);
|
||||
return FutureConverters.asJava(suggestionsRepo.init()).thenAcceptAsync(res -> {}, executor);
|
||||
}
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ import org.enso.logger.JulHandler
|
||||
import org.enso.logger.akka.AkkaConverter
|
||||
import org.enso.polyglot.{HostAccessFactory, RuntimeOptions, RuntimeServerInfo}
|
||||
import org.enso.profiling.events.NoopEventsMonitor
|
||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo}
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.enso.text.{ContentBasedVersioning, Sha3_224VersionCalculator}
|
||||
import org.graalvm.polyglot.Engine
|
||||
import org.graalvm.polyglot.Context
|
||||
@ -63,7 +63,6 @@ import java.io.{File, PrintStream}
|
||||
import java.net.URI
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.time.Clock
|
||||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
/** A main module containing all components of the server.
|
||||
@ -136,9 +135,10 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) {
|
||||
Sha3_224VersionCalculator
|
||||
log.trace("Created Version Calculator [{}].", versionCalculator)
|
||||
|
||||
val sqlDatabase = SqlDatabase.inmem("memdb")
|
||||
|
||||
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
|
||||
val suggestionsRepo =
|
||||
new InMemorySuggestionsRepo()(
|
||||
system.dispatcher
|
||||
);
|
||||
log.trace("Created SQL suggestions repo: [{}].", suggestionsRepo)
|
||||
|
||||
val idlenessMonitor =
|
||||
@ -427,7 +427,6 @@ class MainModule(serverConfig: LanguageServerConfig, logLevel: Level) {
|
||||
system.eventStream,
|
||||
directoriesConfig,
|
||||
jsonRpcProtocolFactory,
|
||||
sqlDatabase,
|
||||
suggestionsRepo,
|
||||
context,
|
||||
zioRuntime
|
||||
|
@ -14,7 +14,7 @@ import org.enso.languageserver.boot.resource.{
|
||||
}
|
||||
import org.enso.languageserver.data.ProjectDirectoriesConfig
|
||||
import org.enso.languageserver.effect
|
||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo}
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.graalvm.polyglot.Context
|
||||
|
||||
import scala.concurrent.ExecutionContextExecutor
|
||||
@ -30,7 +30,6 @@ object ResourcesInitialization {
|
||||
* @param directoriesConfig configuration of directories that should be created
|
||||
* @param protocolFactory the JSON-RPC protocol factory
|
||||
* @param suggestionsRepo the suggestions repo
|
||||
* @param sqlDatabase the sql database
|
||||
* @param truffleContext the runtime context
|
||||
* @param runtime the runtime to run effects
|
||||
* @return the initialization component
|
||||
@ -39,8 +38,7 @@ object ResourcesInitialization {
|
||||
eventStream: EventStream,
|
||||
directoriesConfig: ProjectDirectoriesConfig,
|
||||
protocolFactory: ProtocolFactory,
|
||||
sqlDatabase: SqlDatabase,
|
||||
suggestionsRepo: SqlSuggestionsRepo,
|
||||
suggestionsRepo: InMemorySuggestionsRepo,
|
||||
truffleContext: Context,
|
||||
runtime: effect.Runtime
|
||||
)(implicit ec: ExecutionContextExecutor): InitializationComponent = {
|
||||
@ -54,7 +52,6 @@ object ResourcesInitialization {
|
||||
ec,
|
||||
directoriesConfig,
|
||||
eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
),
|
||||
new TruffleContextInitialization(ec, truffleContext, eventStream)
|
||||
|
@ -569,8 +569,6 @@ class JsonConnectionController(
|
||||
AICompletion -> ai.AICompletionHandler.props(
|
||||
languageServerConfig.aiCompletionConfig
|
||||
),
|
||||
Completion -> search.CompletionHandler
|
||||
.props(requestTimeout, suggestionsHandler),
|
||||
ExecuteExpression -> ExecuteExpressionHandler
|
||||
.props(rpcSession.clientId, requestTimeout, contextRegistry),
|
||||
AttachVisualization -> AttachVisualizationHandler
|
||||
|
@ -1,94 +0,0 @@
|
||||
package org.enso.languageserver.requesthandler.search
|
||||
|
||||
import akka.actor.{Actor, ActorRef, Cancellable, Props, Status}
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||
import org.enso.languageserver.search.SearchApi.{
|
||||
Completion,
|
||||
SuggestionsDatabaseError
|
||||
}
|
||||
import org.enso.languageserver.search.{SearchFailureMapper, SearchProtocol}
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
|
||||
import scala.concurrent.duration.FiniteDuration
|
||||
|
||||
/** A request handler for `search/completion` command.
|
||||
*
|
||||
* @param timeout request timeout
|
||||
* @param suggestionsHandler a reference to the suggestions handler
|
||||
*/
|
||||
class CompletionHandler(
|
||||
timeout: FiniteDuration,
|
||||
suggestionsHandler: ActorRef
|
||||
) extends Actor
|
||||
with LazyLogging
|
||||
with UnhandledLogging {
|
||||
|
||||
import context.dispatcher
|
||||
|
||||
override def receive: Receive = requestStage
|
||||
|
||||
private def requestStage: Receive = {
|
||||
case Request(
|
||||
Completion,
|
||||
id,
|
||||
Completion.Params(file, pos, selfType, returnType, tags, isStatic)
|
||||
) =>
|
||||
suggestionsHandler ! SearchProtocol.Completion(
|
||||
file,
|
||||
pos,
|
||||
selfType,
|
||||
returnType,
|
||||
tags,
|
||||
isStatic
|
||||
)
|
||||
val cancellable =
|
||||
context.system.scheduler.scheduleOnce(timeout, self, RequestTimeout)
|
||||
context.become(responseStage(id, sender(), cancellable))
|
||||
}
|
||||
|
||||
private def responseStage(
|
||||
id: Id,
|
||||
replyTo: ActorRef,
|
||||
cancellable: Cancellable
|
||||
): Receive = {
|
||||
case Status.Failure(ex) =>
|
||||
logger.error("Search completion error.", ex)
|
||||
replyTo ! ResponseError(Some(id), SuggestionsDatabaseError)
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
|
||||
case RequestTimeout =>
|
||||
logger.error("Request [{}] timed out.", id)
|
||||
replyTo ! ResponseError(Some(id), Errors.RequestTimeout)
|
||||
context.stop(self)
|
||||
|
||||
case msg: SearchProtocol.SearchFailure =>
|
||||
replyTo ! ResponseError(Some(id), SearchFailureMapper.mapFailure(msg))
|
||||
|
||||
case SearchProtocol.CompletionResult(version, results) =>
|
||||
replyTo ! ResponseResult(
|
||||
Completion,
|
||||
id,
|
||||
Completion.Result(results, version)
|
||||
)
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
}
|
||||
}
|
||||
|
||||
object CompletionHandler {
|
||||
|
||||
/** Creates configuration object used to create a [[CompletionHandler]].
|
||||
*
|
||||
* @param timeout request timeout
|
||||
* @param suggestionsHandler a reference to the suggestions handler
|
||||
*/
|
||||
def props(
|
||||
timeout: FiniteDuration,
|
||||
suggestionsHandler: ActorRef
|
||||
): Props =
|
||||
Props(new CompletionHandler(timeout, suggestionsHandler))
|
||||
|
||||
}
|
@ -8,7 +8,6 @@ import org.enso.languageserver.filemanager.{FileSystemFailure, Path}
|
||||
import org.enso.pkg.QualifiedName
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.searcher.SuggestionEntry
|
||||
import org.enso.text.editing.model.Position
|
||||
|
||||
object SearchProtocol {
|
||||
|
||||
@ -538,31 +537,6 @@ object SearchProtocol {
|
||||
*/
|
||||
case class GetSuggestionsDatabaseVersionResult(version: Long)
|
||||
|
||||
/** The completion request.
|
||||
*
|
||||
* @param file the edited file
|
||||
* @param position the cursor position
|
||||
* @param selfType filter entries matching the self type
|
||||
* @param returnType filter entries matching the return type
|
||||
* @param tags filter entries by suggestion type
|
||||
* @param isStatic filter entries by `static` field
|
||||
*/
|
||||
case class Completion(
|
||||
file: Path,
|
||||
position: Position,
|
||||
selfType: Option[String],
|
||||
returnType: Option[String],
|
||||
tags: Option[Seq[SuggestionKind]],
|
||||
isStatic: Option[Boolean]
|
||||
)
|
||||
|
||||
/** The reply to the [[Completion]] request.
|
||||
*
|
||||
* @param currentVersion current version of the suggestions database
|
||||
* @param results the list of suggestion ids matched the search query
|
||||
*/
|
||||
case class CompletionResult(currentVersion: Long, results: Seq[SuggestionId])
|
||||
|
||||
/** Base trait for export statements. */
|
||||
sealed trait Export {
|
||||
def module: String
|
||||
|
@ -34,7 +34,6 @@ import org.enso.polyglot.data.TypeGraph
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.searcher.data.QueryResult
|
||||
import org.enso.searcher.SuggestionsRepo
|
||||
import org.enso.text.editing.model.Position
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.Future
|
||||
@ -198,7 +197,7 @@ final class SuggestionsHandler(
|
||||
|
||||
case msg: Api.SuggestionsDatabaseSuggestionsLoadedNotification =>
|
||||
logger.debug(
|
||||
"Starting loading suggestions for library [{}].",
|
||||
"Starting loading suggestions for library [{0}].",
|
||||
msg.libraryName
|
||||
)
|
||||
context.become(
|
||||
@ -213,8 +212,9 @@ final class SuggestionsHandler(
|
||||
.onComplete {
|
||||
case Success(notification) =>
|
||||
logger.debug(
|
||||
"Complete loading suggestions for library [{}].",
|
||||
msg.libraryName
|
||||
"Complete loading suggestions for library [{0}]. Has updates: {1}",
|
||||
msg.libraryName,
|
||||
notification.updates.nonEmpty
|
||||
)
|
||||
if (notification.updates.nonEmpty) {
|
||||
clients.foreach { clientId =>
|
||||
@ -224,7 +224,7 @@ final class SuggestionsHandler(
|
||||
self ! SuggestionsHandler.SuggestionLoadingCompleted
|
||||
case Failure(ex) =>
|
||||
logger.error(
|
||||
"Error applying suggestion updates for loaded library [{}].",
|
||||
"Error applying suggestion updates for loaded library [{0}].",
|
||||
msg.libraryName,
|
||||
ex
|
||||
)
|
||||
@ -373,38 +373,6 @@ final class SuggestionsHandler(
|
||||
)
|
||||
)
|
||||
|
||||
case Completion(path, pos, selfType, returnType, tags, isStatic) =>
|
||||
val selfTypes = selfType.toList.flatMap(ty => ty :: graph.getParents(ty))
|
||||
getModuleName(projectName, path)
|
||||
.flatMap { either =>
|
||||
either.fold(
|
||||
Future.successful,
|
||||
module =>
|
||||
suggestionsRepo
|
||||
.search(
|
||||
Some(module),
|
||||
selfTypes,
|
||||
returnType,
|
||||
tags.map(_.map(SuggestionKind.toSuggestion)),
|
||||
Some(toPosition(pos)),
|
||||
isStatic
|
||||
)
|
||||
.map(CompletionResult.tupled)
|
||||
)
|
||||
}
|
||||
.pipeTo(sender())
|
||||
if (state.shouldStartBackgroundProcessing) {
|
||||
runtimeConnector ! Api.Request(Api.StartBackgroundProcessing())
|
||||
context.become(
|
||||
initialized(
|
||||
projectName,
|
||||
graph,
|
||||
clients,
|
||||
state.backgroundProcessingStarted()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
case FileDeletedEvent(path) =>
|
||||
getModuleName(projectName, path)
|
||||
.flatMap { either =>
|
||||
@ -683,13 +651,6 @@ final class SuggestionsHandler(
|
||||
} yield module
|
||||
}
|
||||
|
||||
/** Convert the internal position representation to the API position.
|
||||
*
|
||||
* @param pos the internal position
|
||||
* @return the API position
|
||||
*/
|
||||
private def toPosition(pos: Position): Suggestion.Position =
|
||||
Suggestion.Position(pos.line, pos.character)
|
||||
}
|
||||
|
||||
object SuggestionsHandler {
|
||||
|
@ -8,15 +8,15 @@ import org.enso.languageserver.data._
|
||||
import org.enso.languageserver.event.InitializedEvent
|
||||
import org.enso.languageserver.filemanager.{ContentRoot, ContentRootWithFile}
|
||||
import org.enso.logger.ReportLogsOnFailure
|
||||
import org.enso.searcher.sql.{SchemaVersion, SqlDatabase, SqlSuggestionsRepo}
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.enso.testkit.{FlakySpec, ToScalaFutureConversions}
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
import org.sqlite.SQLiteException
|
||||
|
||||
import java.nio.file.{Files, StandardOpenOption}
|
||||
import java.nio.file.Files
|
||||
import java.util.UUID
|
||||
import scala.annotation.unused
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
|
||||
@ -41,63 +41,25 @@ class RepoInitializationSpec
|
||||
|
||||
"RepoInitialization" should {
|
||||
|
||||
"initialize repositories" in withDb {
|
||||
(config, sqlDatabase, suggestionsRepo) =>
|
||||
system.eventStream.subscribe(self, classOf[InitializedEvent])
|
||||
"initialize repositories" in withDb { (config, suggestionsRepo) =>
|
||||
system.eventStream.subscribe(self, classOf[InitializedEvent])
|
||||
|
||||
val component =
|
||||
new RepoInitialization(
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
val component =
|
||||
new RepoInitialization(
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
suggestionsRepo
|
||||
)
|
||||
|
||||
val action =
|
||||
for {
|
||||
_ <- component.init()
|
||||
schemaVersion <- suggestionsRepo.getSchemaVersion
|
||||
} yield schemaVersion
|
||||
val resourceInitialization = component.init()
|
||||
Await.result(resourceInitialization, Timeout)
|
||||
|
||||
val version = Await.result(action, Timeout)
|
||||
version shouldEqual SchemaVersion.CurrentVersion
|
||||
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
}
|
||||
|
||||
"recreate suggestion database when schema version is incorrect" in withDb {
|
||||
(config, sqlDatabase, suggestionsRepo) =>
|
||||
system.eventStream.subscribe(self, classOf[InitializedEvent])
|
||||
|
||||
val testSchemaVersion = Long.MaxValue
|
||||
val component =
|
||||
new RepoInitialization(
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
|
||||
sqlDatabase.open()
|
||||
|
||||
val action =
|
||||
for {
|
||||
_ <- suggestionsRepo.init
|
||||
_ <- suggestionsRepo.setSchemaVersion(testSchemaVersion)
|
||||
_ <- component.init()
|
||||
version <- suggestionsRepo.getSchemaVersion
|
||||
} yield version
|
||||
|
||||
val version = Await.result(action, Timeout)
|
||||
version shouldEqual SchemaVersion.CurrentVersion
|
||||
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
}
|
||||
|
||||
"recreate suggestion database when schema version is empty" in withDb {
|
||||
(config, sqlDatabase, suggestionsRepo) =>
|
||||
(config, suggestionsRepo) =>
|
||||
system.eventStream.subscribe(self, classOf[InitializedEvent])
|
||||
|
||||
val component =
|
||||
@ -105,96 +67,19 @@ class RepoInitializationSpec
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
|
||||
// initialize
|
||||
val init =
|
||||
for {
|
||||
_ <- component.init()
|
||||
version <- suggestionsRepo.getSchemaVersion
|
||||
} yield version
|
||||
|
||||
val version1 = Await.result(init, Timeout)
|
||||
version1 shouldEqual SchemaVersion.CurrentVersion
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
|
||||
// remove schema and re-initialize
|
||||
val action =
|
||||
for {
|
||||
_ <- suggestionsRepo.clearSchemaVersion
|
||||
_ <- component.init()
|
||||
version <- suggestionsRepo.getSchemaVersion
|
||||
} yield version
|
||||
_ <- suggestionsRepo.init
|
||||
_ <- component.init()
|
||||
} yield ()
|
||||
|
||||
val version2 = Await.result(action, Timeout)
|
||||
version2 shouldEqual SchemaVersion.CurrentVersion
|
||||
Await.result(action, Timeout)
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
}
|
||||
|
||||
"recreate corrupted suggestion database file" taggedAs Flaky in withConfig {
|
||||
config =>
|
||||
// initialize
|
||||
withRepos(config) { (sqlDatabase, suggestionsRepo) =>
|
||||
val component =
|
||||
new RepoInitialization(
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
|
||||
val init =
|
||||
for {
|
||||
_ <- component.init()
|
||||
version <- suggestionsRepo.getSchemaVersion
|
||||
} yield version
|
||||
|
||||
val version1 = Await.result(init, Timeout)
|
||||
version1 shouldEqual SchemaVersion.CurrentVersion
|
||||
}
|
||||
|
||||
// corrupt
|
||||
val bytes: Array[Byte] = Array(1, 2, 3)
|
||||
Files.delete(config.directories.suggestionsDatabaseFile.toPath)
|
||||
Files.write(
|
||||
config.directories.suggestionsDatabaseFile.toPath,
|
||||
bytes,
|
||||
StandardOpenOption.CREATE
|
||||
)
|
||||
withRepos(config) { (sqlDatabase, suggestionsRepo) =>
|
||||
sqlDatabase.open()
|
||||
an[SQLiteException] should be thrownBy Await.result(
|
||||
suggestionsRepo.getSchemaVersion,
|
||||
Timeout
|
||||
)
|
||||
}
|
||||
|
||||
// re-initialize
|
||||
withRepos(config) { (sqlDatabase, suggestionsRepo) =>
|
||||
val component =
|
||||
new RepoInitialization(
|
||||
system.dispatcher,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
|
||||
val action =
|
||||
for {
|
||||
_ <- component.init()
|
||||
version <- suggestionsRepo.getSchemaVersion
|
||||
} yield version
|
||||
|
||||
val version2 = Await.result(action, Timeout)
|
||||
version2 shouldEqual SchemaVersion.CurrentVersion
|
||||
expectMsg(InitializedEvent.SuggestionsRepoInitialized)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def newConfig(root: ContentRootWithFile): Config = {
|
||||
@ -225,27 +110,21 @@ class RepoInitializationSpec
|
||||
}
|
||||
|
||||
def withRepos(
|
||||
config: Config
|
||||
)(test: (SqlDatabase, SqlSuggestionsRepo) => Any): Unit = {
|
||||
val sqlDatabase = SqlDatabase(config.directories.suggestionsDatabaseFile)
|
||||
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)
|
||||
|
||||
try test(sqlDatabase, suggestionsRepo)
|
||||
finally {
|
||||
sqlDatabase.close()
|
||||
}
|
||||
@unused config: Config
|
||||
)(test: InMemorySuggestionsRepo => Any): Unit = {
|
||||
val suggestionsRepo = new InMemorySuggestionsRepo()
|
||||
test(suggestionsRepo)
|
||||
}
|
||||
|
||||
def withDb(
|
||||
test: (
|
||||
Config,
|
||||
SqlDatabase,
|
||||
SqlSuggestionsRepo
|
||||
InMemorySuggestionsRepo
|
||||
) => Any
|
||||
): Unit = {
|
||||
withConfig { config =>
|
||||
withRepos(config) { (sqlDatabase, suggestionsRepo) =>
|
||||
test(config, sqlDatabase, suggestionsRepo)
|
||||
withRepos(config) { suggestionsRepo =>
|
||||
test(config, suggestionsRepo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,10 +17,9 @@ import org.enso.logger.ReportLogsOnFailure
|
||||
import org.enso.polyglot.data.{Tree, TypeGraph}
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.polyglot.{ExportedSymbol, ModuleExports, Suggestion}
|
||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo}
|
||||
import org.enso.searcher.SuggestionsRepo
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.enso.testkit.RetrySpec
|
||||
import org.enso.text.editing.model.Position
|
||||
import org.scalatest.BeforeAndAfterAll
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpecLike
|
||||
@ -744,7 +743,7 @@ class SuggestionsHandlerSpec
|
||||
expectMsg(SearchProtocol.InvalidateSuggestionsDatabaseResult)
|
||||
}
|
||||
|
||||
"search entries by empty search query" taggedAs Retry in withDb {
|
||||
/* "search entries by empty search query" taggedAs Retry in withDb {
|
||||
(config, repo, _, _, handler) =>
|
||||
val (_, inserted) =
|
||||
Await.result(repo.insertAll(Suggestions.all), Timeout)
|
||||
@ -914,7 +913,7 @@ class SuggestionsHandlerSpec
|
||||
Seq(moduleId, typeId, consId, methodId, localId)
|
||||
)
|
||||
)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
private def fieldUpdate(value: String): SearchProtocol.FieldUpdate[String] =
|
||||
@ -1020,8 +1019,8 @@ class SuggestionsHandlerSpec
|
||||
testContentRoot.toFile
|
||||
)
|
||||
)
|
||||
val sqlDatabase = SqlDatabase(config.directories.suggestionsDatabaseFile)
|
||||
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)
|
||||
val suggestionsRepo =
|
||||
new InMemorySuggestionsRepo()
|
||||
|
||||
val suggestionsInit = suggestionsRepo.init
|
||||
suggestionsInit.onComplete {
|
||||
@ -1056,11 +1055,10 @@ class SuggestionsHandlerSpec
|
||||
testContentRoot.toFile
|
||||
)
|
||||
)
|
||||
val router = TestProbe("session-router")
|
||||
val connector = TestProbe("runtime-connector")
|
||||
val sqlDatabase = SqlDatabase.inmem("testdb")
|
||||
sqlDatabase.open()
|
||||
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)
|
||||
val router = TestProbe("session-router")
|
||||
val connector = TestProbe("runtime-connector")
|
||||
val suggestionsRepo =
|
||||
new InMemorySuggestionsRepo()
|
||||
val handler = newInitializedSuggestionsHandler(
|
||||
config,
|
||||
router,
|
||||
@ -1071,7 +1069,6 @@ class SuggestionsHandlerSpec
|
||||
try test(config, suggestionsRepo, router, connector, handler)
|
||||
finally {
|
||||
system.stop(handler)
|
||||
sqlDatabase.close()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ import org.enso.runtimeversionmanager.test.{
|
||||
FakeEnvironment,
|
||||
TestableThreadSafeFileLockManager
|
||||
}
|
||||
import org.enso.searcher.sql.{SqlDatabase, SqlSuggestionsRepo}
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.enso.testkit.{EitherValue, WithTemporaryDirectory}
|
||||
import org.enso.text.Sha3_224VersionCalculator
|
||||
import org.scalactic.source
|
||||
@ -162,8 +162,7 @@ abstract class BaseServerTest
|
||||
val zioRuntime = new ExecutionContextRuntime(testExecutor)
|
||||
|
||||
val zioExec = ZioExec(zioRuntime)
|
||||
val sqlDatabase = SqlDatabase(config.directories.suggestionsDatabaseFile)
|
||||
val suggestionsRepo = new SqlSuggestionsRepo(sqlDatabase)(system.dispatcher)
|
||||
val suggestionsRepo = new InMemorySuggestionsRepo()(system.dispatcher)
|
||||
|
||||
private def initializationComponent =
|
||||
new SequentialResourcesInitialization(
|
||||
@ -178,7 +177,6 @@ abstract class BaseServerTest
|
||||
initThreadPool,
|
||||
config.directories,
|
||||
system.eventStream,
|
||||
sqlDatabase,
|
||||
suggestionsRepo
|
||||
)
|
||||
)
|
||||
|
@ -897,18 +897,7 @@ class FileManagerTest
|
||||
]
|
||||
},
|
||||
"name" : ".enso",
|
||||
"files" : [
|
||||
{
|
||||
"type" : "File",
|
||||
"name" : "suggestions.db",
|
||||
"path" : {
|
||||
"rootId" : $testContentRootId,
|
||||
"segments" : [
|
||||
".enso"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"files" : [],
|
||||
"directories" : [
|
||||
]
|
||||
},
|
||||
|
@ -1,5 +1,4 @@
|
||||
package org.enso.languageserver.websocket.json
|
||||
import java.util.UUID
|
||||
import io.circe.literal._
|
||||
import org.enso.languageserver.websocket.json.{SearchJsonMessages => json}
|
||||
import org.enso.logger.ReportLogsOnFailure
|
||||
@ -43,93 +42,6 @@ class SuggestionsHandlerTest
|
||||
""")
|
||||
}
|
||||
|
||||
"reply to completion request" taggedAs Flaky in {
|
||||
val client = getInitialisedWsClient()
|
||||
|
||||
client.send(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "search/completion",
|
||||
"id": 0,
|
||||
"params": {
|
||||
"file": {
|
||||
"rootId": $testContentRootId,
|
||||
"segments": [ "src", "Main.enso" ]
|
||||
},
|
||||
"position": {
|
||||
"line": 0,
|
||||
"character": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc" : "2.0",
|
||||
"id" : 0,
|
||||
"result" : {
|
||||
"results" : [
|
||||
],
|
||||
"currentVersion" : 0
|
||||
}
|
||||
}
|
||||
""")
|
||||
|
||||
client.send(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "search/completion",
|
||||
"id": 0,
|
||||
"params": {
|
||||
"file": {
|
||||
"rootId": $testContentRootId,
|
||||
"segments": [ "src", "Foo", "Main.enso" ]
|
||||
},
|
||||
"position": {
|
||||
"line": 0,
|
||||
"character": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc" : "2.0",
|
||||
"id" : 0,
|
||||
"result" : {
|
||||
"results" : [
|
||||
],
|
||||
"currentVersion" : 0
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
"reply with error when project root not found" taggedAs Flaky in {
|
||||
val client = getInitialisedWsClient()
|
||||
|
||||
client.send(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "search/completion",
|
||||
"id": 0,
|
||||
"params": {
|
||||
"file": {
|
||||
"rootId": ${UUID.randomUUID()},
|
||||
"segments": [ "src", "Main.enso" ]
|
||||
},
|
||||
"position": {
|
||||
"line": 0,
|
||||
"character": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc" : "2.0",
|
||||
"id" : 0,
|
||||
"error" : {
|
||||
"code" : 1001,
|
||||
"message" : "Content root not found"
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -447,14 +447,14 @@ class VcsManagerTest
|
||||
"rootId" : $testContentRootId,
|
||||
"segments" : [
|
||||
"src",
|
||||
"Bar.enso"
|
||||
"Foo.enso"
|
||||
]
|
||||
},
|
||||
{
|
||||
"rootId" : $testContentRootId,
|
||||
"segments" : [
|
||||
"src",
|
||||
"Foo.enso"
|
||||
"Bar.enso"
|
||||
]
|
||||
}
|
||||
],
|
||||
|
@ -45,6 +45,7 @@ import scala.collection.immutable.ListSet
|
||||
)
|
||||
)
|
||||
)
|
||||
@SerialVersionUID(9650L)
|
||||
sealed trait Suggestion extends ToLogString {
|
||||
|
||||
def externalId: Option[Suggestion.ExternalID]
|
||||
@ -54,6 +55,23 @@ sealed trait Suggestion extends ToLogString {
|
||||
def documentation: Option[String]
|
||||
|
||||
def withReexports(reexports: Set[String]): Suggestion
|
||||
|
||||
def withReturnType(returnType: String): Suggestion
|
||||
|
||||
/** Creates a copy of this suggestion with the optional fields changed, if applicable.
|
||||
*
|
||||
* @param optExternalId externalID to modify, if non-empty
|
||||
* @param optReturnType return type to modify, if non-empty and applicable
|
||||
* @param optDocumentation documentation to modify, if non-empty
|
||||
* @param optScope scope to modify, if non-empty and applicable
|
||||
* @return a copy of this suggestion with modified fields, if applicable, unchanged suggestion otherwise
|
||||
*/
|
||||
def update(
|
||||
optExternalId: Option[Option[Suggestion.ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Suggestion.Scope]
|
||||
): Suggestion
|
||||
}
|
||||
|
||||
object Suggestion {
|
||||
@ -242,15 +260,29 @@ object Suggestion {
|
||||
override def returnType: String =
|
||||
module
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(module = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
optDocumentation
|
||||
.map(documentation => this.copy(documentation = documentation))
|
||||
.getOrElse(this)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Module(module=$module,name=$name,documentation=" +
|
||||
(if (shouldMask) documentation.map(_ => STUB) else documentation) +
|
||||
s",reexports=$reexports)"
|
||||
|
||||
}
|
||||
|
||||
/** A type definition.
|
||||
@ -276,10 +308,29 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Type(" +
|
||||
@ -317,10 +368,29 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Constructor(" +
|
||||
@ -389,10 +459,29 @@ object Suggestion {
|
||||
@JsonIgnore
|
||||
override def isStatic: Boolean = false
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Getter(" +
|
||||
@ -433,10 +522,29 @@ object Suggestion {
|
||||
) extends Method
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Method(" +
|
||||
@ -484,10 +592,29 @@ object Suggestion {
|
||||
override def name: String =
|
||||
Kind.Conversion.From
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
copy(reexports = reexports)
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Conversion(" +
|
||||
@ -521,10 +648,30 @@ object Suggestion {
|
||||
) extends Suggestion
|
||||
with ToLogString {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
this
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
val v3 = optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
optScope.map(scope => v3.copy(scope = scope)).getOrElse(v3)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
"Function(" +
|
||||
@ -556,10 +703,30 @@ object Suggestion {
|
||||
documentation: Option[String]
|
||||
) extends Suggestion {
|
||||
|
||||
/** @inheritdoc */
|
||||
override def withReexports(reexports: Set[String]): Suggestion =
|
||||
this
|
||||
|
||||
override def withReturnType(returnType: String): Suggestion =
|
||||
copy(returnType = returnType)
|
||||
|
||||
override def update(
|
||||
optExternalId: Option[Option[ExternalID]],
|
||||
optReturnType: Option[String],
|
||||
optDocumentation: Option[Option[String]],
|
||||
optScope: Option[Scope]
|
||||
): Suggestion = {
|
||||
val v1 = optExternalId
|
||||
.map(externalID => this.copy(externalId = externalID))
|
||||
.getOrElse(this)
|
||||
val v2 = optReturnType
|
||||
.map(returnType => v1.copy(returnType = returnType))
|
||||
.getOrElse(v1)
|
||||
val v3 = optDocumentation
|
||||
.map(documentation => v2.copy(documentation = documentation))
|
||||
.getOrElse(v2)
|
||||
optScope.map(scope => v3.copy(scope = scope)).getOrElse(v3)
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def toLogString(shouldMask: Boolean): String =
|
||||
s"Local(" +
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.searcher.sql;
|
||||
|
||||
import org.enso.polyglot.Suggestion;
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
import org.openjdk.jmh.runner.Runner;
|
||||
import org.openjdk.jmh.runner.RunnerException;
|
||||
@ -10,11 +11,11 @@ import scala.collection.immutable.Seq;
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.ExecutionContext;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import scala.jdk.CollectionConverters;
|
||||
import scala.jdk.javaapi.CollectionConverters;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
@ -35,13 +36,11 @@ public class SuggestionsRepoBenchmark {
|
||||
final Seq<Suggestion.Kind> kinds = SuggestionRandom.nextKinds();
|
||||
final Seq<scala.Tuple2<UUID, String>> updateInput = SuggestionRandom.nextUpdateAllInput();
|
||||
|
||||
SqlSuggestionsRepo repo;
|
||||
InMemorySuggestionsRepo repo;
|
||||
|
||||
@Setup
|
||||
public void setup() throws TimeoutException, InterruptedException {
|
||||
var sqlDatabase = SqlDatabase.apply(dbfile.toFile(), none());
|
||||
sqlDatabase.open();
|
||||
repo = new SqlSuggestionsRepo(sqlDatabase, ExecutionContext.global());
|
||||
repo = new InMemorySuggestionsRepo(ExecutionContext.global());
|
||||
if (Files.notExists(dbfile)) {
|
||||
System.out.println("initializing " + dbfile + " ...");
|
||||
Await.ready(repo.init(), TIMEOUT);
|
||||
@ -62,84 +61,16 @@ public class SuggestionsRepoBenchmark {
|
||||
int insertBatch(int size) throws TimeoutException, InterruptedException {
|
||||
Suggestion[] stubs =
|
||||
Stream.generate(SuggestionRandom::nextSuggestion).limit(size).toArray(Suggestion[]::new);
|
||||
return (int) Await.result(repo.insertBatchJava(stubs), TIMEOUT);
|
||||
var scalaList = CollectionConverters.asScala(List.of(stubs));
|
||||
var result = Await.result(repo.insertAll(scalaList.toSeq()), TIMEOUT);
|
||||
return result._2.size();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> scala.Option<T> none() {
|
||||
return (scala.Option<T>) scala.None$.MODULE$;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object searchBaseline() throws TimeoutException, InterruptedException {
|
||||
return Await.result(
|
||||
repo.search(
|
||||
none(),
|
||||
CollectionConverters.ListHasAsScala(new ArrayList<String>()).asScala().toSeq(),
|
||||
none(),
|
||||
none(),
|
||||
none(),
|
||||
none()),
|
||||
TIMEOUT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object searchByReturnType() throws TimeoutException, InterruptedException {
|
||||
return Await.result(
|
||||
repo.search(
|
||||
none(),
|
||||
CollectionConverters.ListHasAsScala(new ArrayList<String>()).asScala().toSeq(),
|
||||
scala.Some.apply("MyType"),
|
||||
none(),
|
||||
none(),
|
||||
none()),
|
||||
TIMEOUT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object searchBySelfType() throws TimeoutException, InterruptedException {
|
||||
var selfTypes = new ArrayList<String>();
|
||||
selfTypes.add("MyType");
|
||||
return Await.result(
|
||||
repo.search(
|
||||
none(),
|
||||
CollectionConverters.ListHasAsScala(selfTypes).asScala().toSeq(),
|
||||
none(),
|
||||
none(),
|
||||
none(),
|
||||
none()),
|
||||
TIMEOUT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object searchBySelfReturnTypes() throws TimeoutException, InterruptedException {
|
||||
var selfTypes = new ArrayList<String>();
|
||||
selfTypes.add("SelfType");
|
||||
return Await.result(
|
||||
repo.search(
|
||||
none(),
|
||||
CollectionConverters.ListHasAsScala(selfTypes).asScala().toSeq(),
|
||||
scala.Some.apply("ReturnType"),
|
||||
none(),
|
||||
none(),
|
||||
none()),
|
||||
TIMEOUT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object searchByAll() throws TimeoutException, InterruptedException {
|
||||
var selfTypes = new ArrayList<String>();
|
||||
selfTypes.add("SelfType");
|
||||
return Await.result(
|
||||
repo.search(
|
||||
none(),
|
||||
CollectionConverters.ListHasAsScala(selfTypes).asScala().toSeq(),
|
||||
scala.Some.apply("ReturnType"),
|
||||
scala.Some.apply(kinds),
|
||||
none(),
|
||||
scala.Some.apply(false)),
|
||||
TIMEOUT);
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public Object updateByExternalId() throws TimeoutException, InterruptedException {
|
||||
return Await.result(repo.updateAll(updateInput), TIMEOUT);
|
||||
|
@ -23,27 +23,6 @@ trait SuggestionsRepo[F[_]] {
|
||||
*/
|
||||
def getAll: F[(Long, Seq[SuggestionEntry])]
|
||||
|
||||
/** Search suggestion by various parameters.
|
||||
*
|
||||
* @param module the module name search parameter
|
||||
* @param selfType the self types to search for, ordered by specificity with
|
||||
* the most specific type first
|
||||
* @param returnType the returnType search parameter
|
||||
* @param kinds the list suggestion kinds to search
|
||||
* @param position the absolute position in the text
|
||||
* @param isStatic the static attribute
|
||||
* @return the current database version and the list of found suggestion ids,
|
||||
* ranked by specificity
|
||||
*/
|
||||
def search(
|
||||
module: Option[String],
|
||||
selfType: Seq[String],
|
||||
returnType: Option[String],
|
||||
kinds: Option[Seq[Suggestion.Kind]],
|
||||
position: Option[Suggestion.Position],
|
||||
isStatic: Option[Boolean]
|
||||
): F[(Long, Seq[Long])]
|
||||
|
||||
/** Select the suggestion by id.
|
||||
*
|
||||
* @param id the id of a suggestion
|
||||
|
@ -0,0 +1,352 @@
|
||||
package org.enso.searcher.memory
|
||||
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.polyglot.Suggestion.ExternalID
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.polyglot.runtime.Runtime.Api.{
|
||||
SuggestionAction,
|
||||
SuggestionsDatabaseAction
|
||||
}
|
||||
import org.enso.searcher.data.QueryResult
|
||||
import org.enso.searcher.sql.SuggestionRowUniqueIndex
|
||||
import org.enso.searcher.{SuggestionEntry, SuggestionsRepo}
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.concurrent.{ExecutionContext, Future}
|
||||
|
||||
class InMemorySuggestionsRepo(implicit ec: ExecutionContext)
|
||||
extends SuggestionsRepo[Future] {
|
||||
private[this] var db: mutable.Map[Long, Suggestion] = null
|
||||
@volatile private[this] var version: Long = 0
|
||||
@volatile private[this] var index: Long = 0
|
||||
|
||||
/** Initialize the repo. */
|
||||
override def init: Future[Unit] = {
|
||||
Future {
|
||||
if (db == null) {
|
||||
db = new mutable.HashMap()
|
||||
version = 0
|
||||
index = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Get current version of the repo. */
|
||||
override def currentVersion: Future[Long] = {
|
||||
Future {
|
||||
version
|
||||
}
|
||||
}
|
||||
|
||||
/** Get all suggestions.
|
||||
*
|
||||
* @return the current database version and the list of suggestions
|
||||
*/
|
||||
override def getAll: Future[(Long, Seq[SuggestionEntry])] = {
|
||||
Future {
|
||||
db.synchronized {
|
||||
(version, db.toSeq.map(v => SuggestionEntry(v._1, v._2)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Select the suggestion by id.
|
||||
*
|
||||
* @param id the id of a suggestion
|
||||
* @return return the suggestion
|
||||
*/
|
||||
override def select(id: Long): Future[Option[Suggestion]] = Future {
|
||||
db.synchronized {
|
||||
db.get(id)
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert the suggestion.
|
||||
*
|
||||
* @param suggestion the suggestion to insert
|
||||
* @return the id of an inserted suggestion
|
||||
*/
|
||||
override def insert(suggestion: Suggestion): Future[Option[Long]] = Future {
|
||||
db.synchronized {
|
||||
val isDuplicate = db.exists(_._2 == suggestion)
|
||||
if (!isDuplicate) {
|
||||
val i = index
|
||||
index += 1
|
||||
db.put(i, suggestion)
|
||||
versionIncrement()
|
||||
Some(i)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Insert a list of suggestions.
|
||||
*
|
||||
* @param suggestions the suggestions to insert
|
||||
* @return the current database version and a list of inserted suggestion ids
|
||||
*/
|
||||
override def insertAll(
|
||||
suggestions: Seq[Suggestion]
|
||||
): Future[(Long, Seq[Long])] = Future {
|
||||
val duplicatesBuilder = Vector.newBuilder[(Suggestion, Suggestion)]
|
||||
val suggestionsMap: mutable.Map[SuggestionRowUniqueIndex, Suggestion] =
|
||||
mutable.LinkedHashMap()
|
||||
suggestions.foreach { suggestion =>
|
||||
val idx = SuggestionRowUniqueIndex(suggestion)
|
||||
suggestionsMap.put(idx, suggestion).foreach { duplicate =>
|
||||
duplicatesBuilder.addOne((duplicate, suggestion))
|
||||
}
|
||||
}
|
||||
val duplicates = duplicatesBuilder.result()
|
||||
if (duplicates.isEmpty) {
|
||||
db.synchronized {
|
||||
val result = suggestions.map(s => {
|
||||
val i = index
|
||||
index += 1
|
||||
db.put(i, s)
|
||||
i
|
||||
})
|
||||
versionIncrement()
|
||||
(version, result)
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("Duplicates detected: " + duplicates)
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply suggestion updates.
|
||||
*
|
||||
* @param tree the sequence of suggestion updates
|
||||
* @return the result of applying the updates
|
||||
*/
|
||||
override def applyTree(
|
||||
tree: Seq[Api.SuggestionUpdate]
|
||||
): Future[Seq[QueryResult[Api.SuggestionUpdate]]] = Future {
|
||||
db.synchronized {
|
||||
val result = tree.map(update =>
|
||||
update.action match {
|
||||
case SuggestionAction.Add() =>
|
||||
// TODO: find duplicates
|
||||
val i = index
|
||||
index += 1
|
||||
db.put(i, update.suggestion)
|
||||
versionIncrement()
|
||||
QueryResult(Seq(i), update)
|
||||
case SuggestionAction.Modify(
|
||||
externalId,
|
||||
arguments,
|
||||
returnType,
|
||||
documentation,
|
||||
scope,
|
||||
reexport
|
||||
) =>
|
||||
if (
|
||||
externalId.nonEmpty || arguments.nonEmpty || returnType.nonEmpty || documentation.nonEmpty || scope.nonEmpty || reexport.nonEmpty
|
||||
) {
|
||||
val suggestionInDb = db.find(_._2 == update.suggestion)
|
||||
suggestionInDb match {
|
||||
case None =>
|
||||
QueryResult(Seq(), update)
|
||||
case Some((suggestionIdx, suggestionInDb)) =>
|
||||
val updatedSuggestion = suggestionInDb.update(
|
||||
externalId,
|
||||
returnType,
|
||||
documentation,
|
||||
scope
|
||||
)
|
||||
if (updatedSuggestion != suggestionInDb) {
|
||||
versionIncrement()
|
||||
}
|
||||
db.put(suggestionIdx, updatedSuggestion)
|
||||
QueryResult(Seq(suggestionIdx), update)
|
||||
}
|
||||
} else {
|
||||
QueryResult(Seq(), update)
|
||||
}
|
||||
case SuggestionAction.Remove() =>
|
||||
val sugestionKey = db.find(_._2 == update.suggestion).map(_._1)
|
||||
sugestionKey.foreach { key =>
|
||||
db.remove(key)
|
||||
versionIncrement()
|
||||
}
|
||||
QueryResult(sugestionKey.toSeq, update)
|
||||
}
|
||||
)
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply the sequence of actions on the database.
|
||||
*
|
||||
* @param actions the list of actions
|
||||
* @return the result of applying the actions
|
||||
*/
|
||||
override def applyActions(
|
||||
actions: Seq[Api.SuggestionsDatabaseAction]
|
||||
): Future[Seq[QueryResult[Api.SuggestionsDatabaseAction]]] = Future {
|
||||
db.synchronized {
|
||||
val result = actions.map {
|
||||
case act @ SuggestionsDatabaseAction.Clean(module) =>
|
||||
val suggestions = db.filter(_._2.module == module)
|
||||
suggestions.foreach { case (id, _) =>
|
||||
db.remove(id)
|
||||
}
|
||||
QueryResult(
|
||||
suggestions.map(_._1),
|
||||
act.asInstanceOf[SuggestionsDatabaseAction]
|
||||
)
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the suggestions related to the export updates.
|
||||
*
|
||||
* @param actions the list of updates
|
||||
* @return the suggestions ids associated with the export updates
|
||||
*/
|
||||
override def getExportedSymbols(
|
||||
actions: Seq[Api.ExportsUpdate]
|
||||
): Future[Seq[QueryResult[Api.ExportsUpdate]]] = Future {
|
||||
db.synchronized {
|
||||
actions.map { action =>
|
||||
val result = action.exports.symbols.toSeq.flatMap { symbol =>
|
||||
db.collectFirst {
|
||||
case (id, suggestion)
|
||||
if suggestion.module == symbol.module &&
|
||||
suggestion.name == symbol.name && Suggestion.Kind(
|
||||
suggestion
|
||||
) == symbol.kind =>
|
||||
id
|
||||
}
|
||||
}
|
||||
QueryResult(result, action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove the suggestion.
|
||||
*
|
||||
* @param suggestion the suggestion to remove
|
||||
* @return the id of removed suggestion
|
||||
*/
|
||||
override def remove(suggestion: Suggestion): Future[Option[Long]] = Future {
|
||||
db.synchronized {
|
||||
val suggestionKey = db.find(_._2 == suggestion).map(_._1)
|
||||
suggestionKey.foreach { id =>
|
||||
db.remove(id)
|
||||
versionIncrement()
|
||||
}
|
||||
suggestionKey
|
||||
}
|
||||
}
|
||||
|
||||
/** Remove suggestions by module names.
|
||||
*
|
||||
* @param modules the list of module names
|
||||
* @return the current database version and a list of removed suggestion ids
|
||||
*/
|
||||
override def removeModules(modules: Seq[String]): Future[(Long, Seq[Long])] =
|
||||
Future {
|
||||
db.synchronized {
|
||||
val suggestions = db.filter {
|
||||
case (_, suggestion: Suggestion)
|
||||
if modules.contains(suggestion.module) =>
|
||||
true
|
||||
case _ => false
|
||||
}
|
||||
suggestions.foreach { case (id, _) =>
|
||||
db.remove(id)
|
||||
|
||||
}
|
||||
condVersionIncrement(suggestions.nonEmpty)
|
||||
(version, suggestions.map(_._1).toSeq)
|
||||
}
|
||||
}
|
||||
|
||||
/** Update the suggestion.
|
||||
*
|
||||
* @param suggestion the key suggestion
|
||||
* @param externalId the external id to update
|
||||
* @param returnType the return type to update
|
||||
* @param documentation the documentation string to update
|
||||
* @param scope the scope to update
|
||||
*/
|
||||
override def update(
|
||||
suggestion: Suggestion,
|
||||
externalId: Option[Option[ExternalID]],
|
||||
returnType: Option[String],
|
||||
documentation: Option[Option[String]],
|
||||
scope: Option[Suggestion.Scope]
|
||||
): Future[(Long, Option[Long])] = Future {
|
||||
db.synchronized {
|
||||
val suggestionEntry = db.find(_._2 == suggestion)
|
||||
val result = suggestionEntry.flatMap { case (idx, oldSuggestion) =>
|
||||
val updated =
|
||||
oldSuggestion.update(externalId, returnType, documentation, scope)
|
||||
if (updated != oldSuggestion) {
|
||||
db.put(idx, updated)
|
||||
Some(idx)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
condVersionIncrement(result.nonEmpty)
|
||||
(version, suggestionEntry.map(_._1))
|
||||
}
|
||||
}
|
||||
|
||||
/** Update a list of suggestions by external id.
|
||||
*
|
||||
* @param expressions pairs of external id and a return type
|
||||
* @return the current database version and a list of updated suggestion ids
|
||||
*/
|
||||
override def updateAll(
|
||||
expressions: Seq[(ExternalID, String)]
|
||||
): Future[(Long, Seq[Option[Long]])] = Future {
|
||||
db.synchronized {
|
||||
val result = expressions.map { case (externalID, expr) =>
|
||||
db.find(e => externalIDMatches(e._2.externalId, externalID)).map {
|
||||
case (idx, suggestion) =>
|
||||
db.put(idx, suggestion.withReturnType(expr))
|
||||
idx
|
||||
}
|
||||
}
|
||||
condVersionIncrement(result.find(_.nonEmpty).nonEmpty)
|
||||
(version, result)
|
||||
}
|
||||
}
|
||||
|
||||
private def versionIncrement(): Unit = {
|
||||
version += 1
|
||||
}
|
||||
|
||||
private def condVersionIncrement(cond: => Boolean): Unit = {
|
||||
if (cond) {
|
||||
version += 1
|
||||
}
|
||||
}
|
||||
|
||||
private def externalIDMatches(
|
||||
existing: Option[ExternalID],
|
||||
externalID: ExternalID
|
||||
): Boolean = {
|
||||
existing
|
||||
.map(e =>
|
||||
e.getLeastSignificantBits == externalID.getLeastSignificantBits && e.getMostSignificantBits == externalID.getMostSignificantBits
|
||||
)
|
||||
.getOrElse(false)
|
||||
}
|
||||
|
||||
/** Cleans the repo resetting the version. */
|
||||
override def clean: Future[Unit] = Future {
|
||||
if (db != null) {
|
||||
db.synchronized {
|
||||
db.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def close(): Unit = {}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package org.enso.searcher.memory
|
||||
|
||||
import org.enso.polyglot.Suggestion
|
||||
import org.enso.searcher.sql.{
|
||||
NameColumn,
|
||||
ScopeColumn,
|
||||
SelfTypeColumn,
|
||||
SuggestionKind,
|
||||
SuggestionRow
|
||||
}
|
||||
|
||||
/** An element of unique suggestion index.
|
||||
*
|
||||
* @param kind the type of a suggestion
|
||||
* @param module the module name
|
||||
* @param name the suggestion name
|
||||
* @param selfType the self type of a suggestion
|
||||
* @param scopeStartLine the line of the start position of the scope
|
||||
* @param scopeStartOffset the offset of the start position of the scope
|
||||
* @param scopeEndLine the line of the end position of the scope
|
||||
* @param scopeEndOffset the offset of the end position of the scope
|
||||
*/
|
||||
final case class SuggestionRowUniqueIndex(
|
||||
kind: Suggestion.Kind,
|
||||
module: String,
|
||||
name: String,
|
||||
selfType: String,
|
||||
scopeStartLine: Int,
|
||||
scopeStartOffset: Int,
|
||||
scopeEndLine: Int,
|
||||
scopeEndOffset: Int
|
||||
)
|
||||
|
||||
object SuggestionRowUniqueIndex {
|
||||
|
||||
/** Create an index element from the provided suggestion.
|
||||
*
|
||||
* @param suggestion the suggestion
|
||||
* @return an index element representing the provided suggestion
|
||||
*/
|
||||
def apply(suggestion: Suggestion): SuggestionRowUniqueIndex = {
|
||||
val scope = Suggestion.Scope(suggestion)
|
||||
val suggestionName = suggestion match {
|
||||
case conversion: Suggestion.Conversion =>
|
||||
NameColumn.conversionMethodName(
|
||||
conversion.selfType,
|
||||
conversion.returnType
|
||||
)
|
||||
case _ => suggestion.name
|
||||
}
|
||||
new SuggestionRowUniqueIndex(
|
||||
Suggestion.Kind(suggestion),
|
||||
suggestion.module,
|
||||
suggestionName,
|
||||
Suggestion.SelfType(suggestion).getOrElse(SelfTypeColumn.EMPTY),
|
||||
scope.map(_.start.line).getOrElse(ScopeColumn.EMPTY),
|
||||
scope.map(_.start.character).getOrElse(ScopeColumn.EMPTY),
|
||||
scope.map(_.end.line).getOrElse(ScopeColumn.EMPTY),
|
||||
scope.map(_.end.character).getOrElse(ScopeColumn.EMPTY)
|
||||
)
|
||||
}
|
||||
|
||||
/** Create an index element from the provided suggestion row.
|
||||
*
|
||||
* @param row the suggestion row
|
||||
* @return an index element representing the provided suggestion row
|
||||
*/
|
||||
def apply(row: SuggestionRow): SuggestionRowUniqueIndex =
|
||||
new SuggestionRowUniqueIndex(
|
||||
SuggestionKind.toSuggestion(row.kind),
|
||||
row.module,
|
||||
row.name,
|
||||
row.selfType,
|
||||
row.scopeStartLine,
|
||||
row.scopeStartOffset,
|
||||
row.scopeEndLine,
|
||||
row.scopeEndOffset
|
||||
)
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
package org.enso.searcher.sql
|
||||
|
||||
/** An error indicating that the database has invalid schema version.
|
||||
*
|
||||
* @param version the database schema version.
|
||||
*/
|
||||
class InvalidSchemaVersion(val version: Long)
|
||||
extends RuntimeException(
|
||||
s"Database schema version '$version' is different from the application " +
|
||||
s"schema version '${SchemaVersion.CurrentVersion}'."
|
||||
)
|
@ -1,108 +0,0 @@
|
||||
package org.enso.searcher.sql
|
||||
|
||||
import java.io.File
|
||||
|
||||
import com.typesafe.config.{Config, ConfigFactory}
|
||||
import org.enso.searcher.Database
|
||||
import org.enso.searcher.sqlite.LockingMode
|
||||
import slick.dbio.DBIO
|
||||
import slick.jdbc.SQLiteProfile
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
/** Ths SQL database that runs Slick [[DBIO]] queries resulting in a [[Future]].
|
||||
*
|
||||
* @param config the configuration
|
||||
*/
|
||||
final class SqlDatabase(config: Option[Config] = None)
|
||||
extends Database[DBIO, Future] {
|
||||
|
||||
private var db: SQLiteProfile.backend.Database = _
|
||||
|
||||
/** @inheritdoc */
|
||||
override def run[A](query: DBIO[A]): Future[A] =
|
||||
db.run(query)
|
||||
|
||||
/** @inheritdoc */
|
||||
override def open(): Unit =
|
||||
this.synchronized {
|
||||
if (db eq null) {
|
||||
db = SQLiteProfile.backend.Database
|
||||
.forConfig(
|
||||
SqlDatabase.configPath,
|
||||
config.orNull,
|
||||
classLoader = getClass.getClassLoader
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
override def close(): Unit =
|
||||
this.synchronized {
|
||||
if (db ne null) {
|
||||
db.close()
|
||||
db = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object SqlDatabase {
|
||||
|
||||
private val configPath: String =
|
||||
"searcher.db"
|
||||
|
||||
/** Create in-memory [[SqlDatabase]] instance.
|
||||
*
|
||||
* @return new sql database instance
|
||||
*/
|
||||
def inmem(name: String): SqlDatabase =
|
||||
fromUrl(inmemUrl(name))
|
||||
|
||||
/** Create [[SqlDatabase]] instance.
|
||||
*
|
||||
* @param filename the database file path
|
||||
* @param maybeLockingMode the locking mode
|
||||
* @return new sql database instance
|
||||
*/
|
||||
def apply(
|
||||
filename: File,
|
||||
maybeLockingMode: Option[LockingMode] = None
|
||||
): SqlDatabase =
|
||||
fromUrl(jdbcUrl(filename.toString, maybeLockingMode))
|
||||
|
||||
/** Create [[SqlDatabase]] instance.
|
||||
*
|
||||
* @param url the database url
|
||||
* @return new sql database instance
|
||||
*/
|
||||
def fromUrl(url: String): SqlDatabase = {
|
||||
val config = ConfigFactory
|
||||
.parseString(s"""$configPath.url = "$url"""")
|
||||
.withFallback(
|
||||
ConfigFactory.load(getClass.getClassLoader)
|
||||
)
|
||||
new SqlDatabase(Some(config))
|
||||
}
|
||||
|
||||
/** Create JDBC URL for in-memory database. */
|
||||
private def inmemUrl(name: String): String =
|
||||
s"jdbc:sqlite:file:$name?mode=memory&cache=shared"
|
||||
|
||||
/** Create JDBC URL from the file path. */
|
||||
private def jdbcUrl(
|
||||
filename: String,
|
||||
maybeLockingMode: Option[LockingMode]
|
||||
): String = {
|
||||
maybeLockingMode match {
|
||||
case None =>
|
||||
s"jdbc:sqlite:${escapePath(filename)}"
|
||||
|
||||
case Some(lockingMode) =>
|
||||
s"jdbc:sqlite:file:${escapePath(filename)}?vfs=${lockingMode.name}"
|
||||
}
|
||||
}
|
||||
|
||||
/** Escape Windows path. */
|
||||
private def escapePath(path: String): String =
|
||||
path.replace("\\", "\\\\")
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,6 @@
|
||||
package org.enso.searcher.sql
|
||||
|
||||
import org.enso.polyglot.Suggestion
|
||||
import slick.jdbc.SQLiteProfile.api._
|
||||
|
||||
import scala.annotation.nowarn
|
||||
|
||||
/** A row in the suggestions table.
|
||||
*
|
||||
@ -41,18 +38,6 @@ case class SuggestionRow(
|
||||
documentation: Option[String]
|
||||
)
|
||||
|
||||
/** A row in the suggestions_version table.
|
||||
*
|
||||
* @param id the row id
|
||||
*/
|
||||
case class SuggestionsVersionRow(id: Option[Long])
|
||||
|
||||
/** A row in the schema_version table.
|
||||
*
|
||||
* @param id the row id
|
||||
*/
|
||||
case class SchemaVersionRow(id: Option[Long])
|
||||
|
||||
/** The type of a suggestion. */
|
||||
object SuggestionKind {
|
||||
|
||||
@ -118,77 +103,6 @@ object NameColumn {
|
||||
|
||||
}
|
||||
|
||||
/** The schema of the suggestions table. */
|
||||
@nowarn("msg=multiarg infix syntax")
|
||||
final class SuggestionsTable(tag: Tag)
|
||||
extends Table[SuggestionRow](tag, "suggestions") {
|
||||
|
||||
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
|
||||
def externalIdLeast = column[Option[Long]]("external_id_least")
|
||||
def externalIdMost = column[Option[Long]]("external_id_most")
|
||||
def kind = column[Byte]("kind")
|
||||
def module = column[String]("module")
|
||||
def name = column[String]("name")
|
||||
def selfType = column[String]("self_type")
|
||||
def returnType = column[String]("return_type")
|
||||
def parentType = column[Option[String]]("parent_type")
|
||||
def isStatic = column[Boolean]("is_static")
|
||||
def scopeStartLine =
|
||||
column[Int]("scope_start_line", O.Default(ScopeColumn.EMPTY))
|
||||
def scopeStartOffset =
|
||||
column[Int]("scope_start_offset", O.Default(ScopeColumn.EMPTY))
|
||||
def scopeEndLine =
|
||||
column[Int]("scope_end_line", O.Default(ScopeColumn.EMPTY))
|
||||
def scopeEndOffset =
|
||||
column[Int]("scope_end_offset", O.Default(ScopeColumn.EMPTY))
|
||||
def documentation = column[Option[String]]("documentation")
|
||||
|
||||
def * =
|
||||
(
|
||||
id.?,
|
||||
externalIdLeast,
|
||||
externalIdMost,
|
||||
kind,
|
||||
module,
|
||||
name,
|
||||
selfType,
|
||||
returnType,
|
||||
parentType,
|
||||
isStatic,
|
||||
scopeStartLine,
|
||||
scopeStartOffset,
|
||||
scopeEndLine,
|
||||
scopeEndOffset,
|
||||
documentation
|
||||
) <>
|
||||
(SuggestionRow.tupled, SuggestionRow.unapply)
|
||||
|
||||
def moduleIdx = index("suggestions_module_idx", module)
|
||||
def name_idx = index("suggestions_name_idx", name)
|
||||
def selfTypeIdx = index("suggestions_self_type_idx", selfType)
|
||||
def returnTypeIdx = index("suggestions_return_type_idx", returnType)
|
||||
def externalIdIdx =
|
||||
index("suggestions_external_id_idx", (externalIdLeast, externalIdMost))
|
||||
|
||||
// NOTE: unique index should not contain nullable columns because SQLite
|
||||
// teats NULLs as distinct values.
|
||||
def uniqueIdx =
|
||||
index(
|
||||
"suggestions_unique_idx",
|
||||
(
|
||||
kind,
|
||||
module,
|
||||
name,
|
||||
selfType,
|
||||
scopeStartLine,
|
||||
scopeStartOffset,
|
||||
scopeEndLine,
|
||||
scopeEndOffset
|
||||
),
|
||||
unique = true
|
||||
)
|
||||
}
|
||||
|
||||
/** An element of unique suggestion index.
|
||||
*
|
||||
* @param kind the type of a suggestion
|
||||
@ -257,33 +171,3 @@ object SuggestionRowUniqueIndex {
|
||||
row.scopeEndOffset
|
||||
)
|
||||
}
|
||||
|
||||
/** The schema of the suggestions_version table. */
|
||||
@nowarn("msg=multiarg infix syntax")
|
||||
final class SuggestionsVersionTable(tag: Tag)
|
||||
extends Table[SuggestionsVersionRow](tag, "suggestions_version") {
|
||||
|
||||
def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
|
||||
|
||||
def * = id.? <> (SuggestionsVersionRow.apply, SuggestionsVersionRow.unapply)
|
||||
}
|
||||
|
||||
/** The schema of the schema_version table. */
|
||||
@nowarn("msg=multiarg infix syntax")
|
||||
final class SchemaVersionTable(tag: Tag)
|
||||
extends Table[SchemaVersionRow](tag, "schema_version") {
|
||||
|
||||
def id = column[Long]("id", O.PrimaryKey)
|
||||
|
||||
def * = id.? <> (SchemaVersionRow.apply, SchemaVersionRow.unapply)
|
||||
}
|
||||
|
||||
object Suggestions extends TableQuery(new SuggestionsTable(_))
|
||||
|
||||
object SuggestionsVersion extends TableQuery(new SuggestionsVersionTable(_))
|
||||
|
||||
object SchemaVersion extends TableQuery(new SchemaVersionTable(_)) {
|
||||
|
||||
/** The current schema version. */
|
||||
val CurrentVersion: Long = 12
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import org.enso.polyglot.{ExportedSymbol, ModuleExports, Suggestion}
|
||||
import org.enso.polyglot.runtime.Runtime.Api
|
||||
import org.enso.searcher.SuggestionEntry
|
||||
import org.enso.searcher.data.QueryResult
|
||||
import org.enso.searcher.sql.SqlSuggestionsRepo.UniqueConstraintViolatedError
|
||||
import org.enso.searcher.memory.InMemorySuggestionsRepo
|
||||
import org.enso.searcher.sql.equality.SuggestionsEquality
|
||||
import org.enso.testkit.RetrySpec
|
||||
import org.scalactic.TripleEqualsSupport
|
||||
@ -13,7 +13,6 @@ import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import java.nio.file.{Files, Path}
|
||||
import java.util.UUID
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.ExecutionContext.Implicits.global
|
||||
import scala.concurrent.duration._
|
||||
@ -38,11 +37,8 @@ class SuggestionsRepoTest
|
||||
tmp
|
||||
}
|
||||
|
||||
def withRepo(test: SqlSuggestionsRepo => Any): Any = {
|
||||
val tmpdb = Files.createTempFile(tmpdir, "suggestions-repo", ".db")
|
||||
val sqlDatabase = SqlDatabase(tmpdb.toFile)
|
||||
sqlDatabase.open()
|
||||
val repo = new SqlSuggestionsRepo(sqlDatabase)
|
||||
def withRepo(test: InMemorySuggestionsRepo => Any): Any = {
|
||||
val repo = new InMemorySuggestionsRepo()
|
||||
Await.ready(repo.init, Timeout)
|
||||
try test(repo)
|
||||
finally {
|
||||
@ -57,26 +53,13 @@ class SuggestionsRepoTest
|
||||
Await.result(repo.init, Timeout)
|
||||
}
|
||||
|
||||
"check the schema version when init" taggedAs Retry in withRepo { repo =>
|
||||
val wrongSchemaVersion = Long.MinValue
|
||||
val action =
|
||||
for {
|
||||
version <- repo.setSchemaVersion(wrongSchemaVersion)
|
||||
_ <- repo.init
|
||||
} yield version
|
||||
|
||||
val thrown =
|
||||
the[InvalidSchemaVersion] thrownBy Await.result(action, Timeout)
|
||||
thrown.version shouldEqual wrongSchemaVersion
|
||||
}
|
||||
|
||||
"insert all suggestions" taggedAs Retry in withRepo { repo =>
|
||||
val action =
|
||||
for {
|
||||
v1 <- repo.currentVersion
|
||||
(v2, ids) <- repo.insertAll(suggestion.all)
|
||||
all <- repo.selectAllSuggestions
|
||||
} yield (ids, all, v1, v2)
|
||||
all <- repo.getAll
|
||||
} yield (ids, all._2, v1, v2)
|
||||
|
||||
val (ids, entries, v1, v2) = Await.result(action, Timeout)
|
||||
val expectedEntries = ids.zip(suggestion.all).map(SuggestionEntry.tupled)
|
||||
@ -102,7 +85,7 @@ class SuggestionsRepoTest
|
||||
_ <- repo.insertAll(Seq(suggestion.local, suggestion.local))
|
||||
} yield ()
|
||||
|
||||
an[UniqueConstraintViolatedError] should be thrownBy Await.result(
|
||||
an[RuntimeException] should be thrownBy Await.result(
|
||||
action,
|
||||
Timeout
|
||||
)
|
||||
@ -708,600 +691,6 @@ class SuggestionsRepoTest
|
||||
QueryResult(Seq(ids(3)), updates(1))
|
||||
)
|
||||
}
|
||||
|
||||
"search suggestion by empty query" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(None, Seq(), None, None, None, None)
|
||||
} yield res._2
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
}
|
||||
|
||||
"search suggestion by module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
id5 <- repo.insert(suggestion.function)
|
||||
id6 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
Some("local.Test.Main"),
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, id5, id6, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, id5, id6, res) =
|
||||
Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(
|
||||
id0,
|
||||
id1,
|
||||
id2,
|
||||
id3,
|
||||
id4,
|
||||
id5,
|
||||
id6
|
||||
).flatten
|
||||
}
|
||||
|
||||
"search suggestion by empty module" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(Some(""), Seq(), None, None, None, None)
|
||||
} yield (res._2, Seq(id0, id1, id2, id3, id4))
|
||||
|
||||
val (res, globals) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs globals.flatten
|
||||
}
|
||||
|
||||
"search suggestion by self type" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(None, Seq("local.Test.Main"), None, None, None, None)
|
||||
} yield (id2, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by self type excluding constructors" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val constructorSelfType =
|
||||
Suggestion.SelfType(suggestion.constructor).toSeq
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
constructorSelfType,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield res._2
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
}
|
||||
|
||||
"search suggestion by return type" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
id3 <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
Some("local.Test.Main.MyType"),
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (id3, id4, res._2)
|
||||
|
||||
val (id1, id2, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2).flatten
|
||||
}
|
||||
|
||||
"search suggestion by kind" taggedAs Retry in withRepo { repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Constructor, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
id1 <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(None, Seq(), None, Some(kinds), None, None)
|
||||
} yield (id1, id4, res._2)
|
||||
|
||||
val (id1, id2, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2).flatten
|
||||
}
|
||||
|
||||
"search suggestion by empty kinds" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(None, Seq(), None, Some(Seq()), None, None)
|
||||
} yield res._2
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
}
|
||||
|
||||
"search suggestion outside of scope" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
Some(Suggestion.Position(99, 42)),
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id0, id1, id2, id3, id4).flatten
|
||||
}
|
||||
|
||||
"search suggestion by scope begin" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
id5 <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
Some(Suggestion.Position(1, 5)),
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, id5, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, id5, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(
|
||||
id0,
|
||||
id1,
|
||||
id2,
|
||||
id3,
|
||||
id4,
|
||||
id5
|
||||
).flatten
|
||||
}
|
||||
|
||||
"search suggestion by scope end" taggedAs Retry in withRepo { repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
id5 <- repo.insert(suggestion.function)
|
||||
id6 <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
Some(Suggestion.Position(6, 0)),
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, id5, id6, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, id5, id6, res) =
|
||||
Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(
|
||||
id0,
|
||||
id1,
|
||||
id2,
|
||||
id3,
|
||||
id4,
|
||||
id5,
|
||||
id6
|
||||
).flatten
|
||||
}
|
||||
|
||||
"search suggestion inside the function scope" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
id5 <- repo.insert(suggestion.function)
|
||||
id6 <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
Some(Suggestion.Position(2, 0)),
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, id5, id6, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, id5, _, res) =
|
||||
Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(
|
||||
id0,
|
||||
id1,
|
||||
id2,
|
||||
id3,
|
||||
id4,
|
||||
id5
|
||||
).flatten
|
||||
}
|
||||
|
||||
"search suggestion inside the local scope" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.conversion)
|
||||
id5 <- repo.insert(suggestion.function)
|
||||
id6 <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
Some(Suggestion.Position(4, 0)),
|
||||
None
|
||||
)
|
||||
} yield (id0, id1, id2, id3, id4, id5, id6, res._2)
|
||||
|
||||
val (id0, id1, id2, id3, id4, id5, id6, res) =
|
||||
Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(
|
||||
id0,
|
||||
id1,
|
||||
id2,
|
||||
id3,
|
||||
id4,
|
||||
id5,
|
||||
id6
|
||||
).flatten
|
||||
}
|
||||
|
||||
"search suggestion by the static attribute" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
id0 <- repo.insert(suggestion.module)
|
||||
id1 <- repo.insert(suggestion.tpe)
|
||||
id2 <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.instanceMethod)
|
||||
id5 <- repo.insert(suggestion.conversion)
|
||||
id6 <- repo.insert(suggestion.function)
|
||||
id7 <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(true)
|
||||
)
|
||||
} yield (Seq(id0, id1, id2, id3, id5, id6, id7), res._2)
|
||||
|
||||
val (ids, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs ids.flatten
|
||||
}
|
||||
|
||||
"search suggestion by module and self type" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
id2 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
Some("local.Test.Main"),
|
||||
Seq("local.Test.Main"),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (id2, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by self type and static attribute" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
id3 <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(suggestion.method.selfType),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(true)
|
||||
)
|
||||
} yield (id3, res._2)
|
||||
|
||||
val (id3, res) =
|
||||
Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id3).flatten
|
||||
}
|
||||
|
||||
"search suggestion by self-type and non-static attribute" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
id4 <- repo.insert(suggestion.instanceMethod)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <-
|
||||
repo.search(
|
||||
None,
|
||||
Seq(suggestion.instanceMethod.selfType),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
Some(false)
|
||||
)
|
||||
} yield (Seq(id4), res._2)
|
||||
|
||||
val (ids, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs ids.flatten
|
||||
}
|
||||
|
||||
"search suggestion by return type and kind" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Constructor, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
Some("local.Test.Main.MyType"),
|
||||
Some(kinds),
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (id4, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by return type and scope" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
id3 <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
Some("local.Test.Main.MyType"),
|
||||
None,
|
||||
Some(Suggestion.Position(6, 0)),
|
||||
None
|
||||
)
|
||||
} yield (id3, id4, res._2)
|
||||
|
||||
val (id1, id2, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id1, id2).flatten
|
||||
}
|
||||
|
||||
"search suggestion by kind and scope" taggedAs Retry in withRepo { repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Constructor, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
id1 <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
None,
|
||||
Some(kinds),
|
||||
Some(Suggestion.Position(99, 1)),
|
||||
None
|
||||
)
|
||||
} yield (id1, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by self and return types" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq("local.Test.Main"),
|
||||
Some("local.Test.Main.MyType"),
|
||||
None,
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield res._2
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
}
|
||||
|
||||
"search suggestion by module, return type and kind" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Constructor, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
Some("local.Test.Main"),
|
||||
Seq(),
|
||||
Some("local.Test.Main.MyType"),
|
||||
Some(kinds),
|
||||
None,
|
||||
None
|
||||
)
|
||||
} yield (id4, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by return type, kind and scope" taggedAs Retry in withRepo {
|
||||
repo =>
|
||||
val kinds = Seq(Suggestion.Kind.Constructor, Suggestion.Kind.Local)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
id4 <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
None,
|
||||
Seq(),
|
||||
Some("local.Test.Main.MyType"),
|
||||
Some(kinds),
|
||||
Some(Suggestion.Position(6, 0)),
|
||||
None
|
||||
)
|
||||
} yield (id4, res._2)
|
||||
|
||||
val (id, res) = Await.result(action, Timeout)
|
||||
res should contain theSameElementsAs Seq(id).flatten
|
||||
}
|
||||
|
||||
"search suggestion by all parameters" taggedAs Retry in withRepo { repo =>
|
||||
val kinds = Seq(
|
||||
Suggestion.Kind.Constructor,
|
||||
Suggestion.Kind.Method,
|
||||
Suggestion.Kind.Function
|
||||
)
|
||||
val action = for {
|
||||
_ <- repo.insert(suggestion.module)
|
||||
_ <- repo.insert(suggestion.tpe)
|
||||
_ <- repo.insert(suggestion.constructor)
|
||||
_ <- repo.insert(suggestion.method)
|
||||
_ <- repo.insert(suggestion.conversion)
|
||||
_ <- repo.insert(suggestion.function)
|
||||
_ <- repo.insert(suggestion.local)
|
||||
res <- repo.search(
|
||||
Some("local.Test.Main"),
|
||||
Seq("local.Test.Main"),
|
||||
Some("local.Test.Main.MyType"),
|
||||
Some(kinds),
|
||||
Some(Suggestion.Position(42, 0)),
|
||||
Some(true)
|
||||
)
|
||||
} yield res._2
|
||||
|
||||
val res = Await.result(action, Timeout)
|
||||
res.isEmpty shouldEqual true
|
||||
}
|
||||
}
|
||||
|
||||
object suggestion {
|
||||
|
@ -1 +0,0 @@
|
||||
LICENSE.txt
|
@ -1,25 +0,0 @@
|
||||
Copyright 2011-2021 Lightbend, Inc.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
@ -0,0 +1 @@
|
||||
this code has waived all copyright and related or neighboring *
|
@ -1 +0,0 @@
|
||||
See https://github.com/reactive-streams/reactive-streams-jvm for more information.
|
@ -1,5 +0,0 @@
|
||||
Copyright 2008 Taro L. Saito
|
||||
Copyright 2009 Taro L. Saito
|
||||
Copyright 2010 Taro L. Saito
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
this work for additional information regarding copyright ownership.
|
@ -1,4 +0,0 @@
|
||||
Copyright (c) 2007 David Crawshaw <david@zentus.com>
|
||||
Copyright (c) 2021 Gauthier Roebroeck <gauthier.roebroeck@gmail.com>
|
||||
Copyright 2007 Taro L. Saito
|
||||
Copyright 2016 Magnus Reftel
|
@ -1 +0,0 @@
|
||||
LICENSE
|
@ -1,2 +0,0 @@
|
||||
META-INF/maven/org.xerial/sqlite-jdbc/LICENSE
|
||||
META-INF/maven/org.xerial/sqlite-jdbc/LICENSE.zentus
|
@ -1,3 +1,3 @@
|
||||
04F26D14EEE87D86068545C263A1459FF8E8E7AB7E5AB92AD65A35A61E3AD3F2
|
||||
27B287816C14571C5032AE4D27CC680B74E99DB22BA2134FE24BB8141A53426D
|
||||
40308641443E112D3668A68FF82D969D26B6D07D48724AFE5119B838FCF36639
|
||||
119423CD9496FC62BB306497C105ED326E25317490487D577212D1230F6D2D40
|
||||
0
|
||||
|
Loading…
Reference in New Issue
Block a user