diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -1,23 +1,22 @@ syntax: glob *~ *.class *.jar *.marks *.orig *.rej .DS_Store .swp syntax: regexp ^contrib ^heaps/ ^browser_info/ ^doc/.*\.pdf ^lib/classes/ -^src/Tools/jEdit/dist/ ^src/Tools/VSCode/out/ ^src/Tools/VSCode/extension/node_modules/ ^Admin/jenkins/ci-extras/target/ diff --git a/Admin/Release/CHECKLIST b/Admin/Release/CHECKLIST --- a/Admin/Release/CHECKLIST +++ b/Admin/Release/CHECKLIST @@ -1,99 +1,99 @@ Checklist for official releases =============================== - check latest updates of polyml, jdk, scala, jedit; - check Admin/components; - test "isabelle dump -b Pure ZF"; - test "isabelle build -o export_theory -f ZF"; - test "isabelle server" according to "system" manual; - test Isabelle/VSCode; - test Isabelle/jEdit: print buffer - test "#!/usr/bin/env isabelle_scala_script"; - test Windows 10 subsystem for Linux: https://docs.microsoft.com/en-us/windows/wsl/install-win10 - check (non-)executable files: $ find . "(" -name "*.thy" -o -name "*.ML" -o -name "*.scala" -o -name ROOT ")" -executable $ find -type f -executable - check sources: isabelle check_sources '~~' '$AFP_BASE' - check ANNOUNCE, README, NEWS, COPYRIGHT, CONTRIBUTORS; - check versions: src/Tools/jEdit/src/Isabelle.props src/Tools/jEdit/src-base/Isabelle_Base.props - check Isabelle version: src/Tools/VSCode/extension/README.md src/Tools/VSCode/extension/package.json - check funny base directory, e.g. "Test 中国"; - diff NEWS wrt. last official release, which is read-only; - update https://isabelle.sketis.net/repos/isabelle-website -- check doc/Contents, src/Tools/jEdit/dist/doc/Contents; +- check doc/Contents, $JEDIT_HOME/doc/Contents; - test old HD display: Linux, Windows, macOS; - macOS: check recent MacTeX; - Windows: check recent MiKTeX; - Phabricator: . src/Doc/System/Phabricator.thy: check/update underlying Ubuntu version . etc/options: check/update phabricator_version entries; Repository fork =============== - isabelle: finalize NEWS / CONTRIBUTORS -- proper headers for named release; - isabelle-release: hg tag; - isabelle: back to post-release mode -- after fork point; Packaging ========= - macOS: provide "gnutar" executable via shell PATH (e.g. copy of /usr/bin/gnutar from Mountain Lion) - fully-automated packaging (e.g. on lxcisa0): hg up -r DISTNAME && Admin/build_release -D /home/isabelle/dist -b HOL -l -R DISTNAME - Docker image: isabelle build_docker -o Dockerfile -E -t makarius/isabelle:Isabelle2021 Isabelle2021_linux.tar.gz docker login docker push makarius/isabelle:Isabelle2021 docker tag ... latest docker push makarius/isabelle:latest https://hub.docker.com/r/makarius/isabelle https://docs.docker.com/engine/reference/commandline/push Post-release ============ - update Admin/Release/official - update /home/isabelle and /home/isabelle/html-data diff --git a/Admin/Windows/launch4j/isabelle.xml b/Admin/Windows/launch4j/isabelle.xml --- a/Admin/Windows/launch4j/isabelle.xml +++ b/Admin/Windows/launch4j/isabelle.xml @@ -1,41 +1,41 @@ true gui {OUTFILE} normal false false {ICON} - isabelle.Main + isabelle.jedit.Main {CLASSPATH} {ISABELLE_NAME} {ISABELLE_NAME} %EXEDIR%\contrib\jdk\x86_64-windows true false jdkOnly 64 -splash:"%EXEDIR%\lib\logo\isabelle.gif" -Disabelle.root="%EXEDIR%" -Dcygwin.root="%EXEDIR%\contrib\cygwin" {SPLASH} true 120 false \ No newline at end of file diff --git a/Admin/build b/Admin/build --- a/Admin/build +++ b/Admin/build @@ -1,91 +1,88 @@ #!/usr/bin/env bash # # Administrative build for Isabelle source distribution. ## directory layout if [ -z "$ISABELLE_HOME" ]; then unset CDPATH ISABELLE_HOME="$(cd "$(dirname "$0")"; cd "$(pwd -P)"; cd ..; pwd)" ISABELLE_TOOL="$ISABELLE_HOME/bin/isabelle" fi ## diagnostics PRG="$(basename "$0")" function usage() { cat <&2 exit 2 } ## process command line [ "$#" -eq 0 ] && usage MODULES="$@"; shift "$#" ## modules function build_all () { build_browser - build_jars + build_setup build } function build_browser () { pushd "$ISABELLE_HOME/lib/browser" >/dev/null "$ISABELLE_TOOL" env ./build || exit $? popd >/dev/null } -function build_jars () +function build_setup () { - pushd "$ISABELLE_HOME" >/dev/null - "$ISABELLE_TOOL" env src/Pure/build-jars "$@" || exit $? - popd >/dev/null + rm -rf \ + "$ISABELLE_HOME/lib/classes/Pure.jar" \ + "$ISABELLE_HOME/lib/classes/Pure.shasum" \ + "$ISABELLE_HOME/src/Tools/jEdit/dist" + "$ISABELLE_TOOL" java isabelle.setup.Setup "$@" } ## main -#FIXME workarounds for scalac 2.11.0 -export CYGWIN="nodosfilewarning" -function stty() { :; } -export -f stty - for MODULE in $MODULES do case $MODULE in all) build_all;; browser) build_browser;; - jars) build_jars;; - jars_fresh) build_jars -f;; + jars) build_setup build;; + jars_fresh) build_setup build_fresh;; *) fail "Bad module $MODULE" esac done diff --git a/Admin/components/components.sha1 b/Admin/components/components.sha1 --- a/Admin/components/components.sha1 +++ b/Admin/components/components.sha1 @@ -1,419 +1,421 @@ 59a71e08c34ff01f3f5c4af00db5e16369527eb7 Haskabelle-2013.tar.gz 23a96ff4951d72f4024b6e8843262eda988bc151 Haskabelle-2014.tar.gz eccff31931fb128c1dd522cfc85495c9b66e67af Haskabelle-2015.tar.gz ed740867925dcf58692c8d3e350c28e3b4d4a60f Isabelle_app-20210126.tar.gz 8ee375cfc38972f080dbc78f07b68dac03efe968 ProofGeneral-3.7.1.1.tar.gz 847b52c0676b5eb0fbf0476f64fc08c2d72afd0c ProofGeneral-4.1.tar.gz 8e0b2b432755ef11d964e20637d1bc567d1c0477 ProofGeneral-4.2-1.tar.gz 51e1e0f399e934020565b2301358452c0bcc8a5e ProofGeneral-4.2-2.tar.gz 8472221c876a430cde325841ce52893328302712 ProofGeneral-4.2.tar.gz fbe83b522cb37748ac1b3c943ad71704fdde2f82 bash_process-1.1.1.tar.gz bb9ef498cd594b4289221b96146d529c899da209 bash_process-1.1.tar.gz 81250148f8b89ac3587908fb20645081d7f53207 bash_process-1.2.1.tar.gz 97b2491382130a841b3bbaebdcf8720c4d4fb227 bash_process-1.2.2.tar.gz 5c5b7c18cc1dc2a4d22b997dac196da09eaca868 bash_process-1.2.3-1.tar.gz 48b01bd9436e243ffcb7297f08b498d0c0875ed9 bash_process-1.2.3.tar.gz 11815d5f3af0de9022e903ed8702c136591f06fe bash_process-1.2.4-1.tar.gz 729486311833e4eff0fbf2d8041dddad520ca88c bash_process-1.2.4-2.tar.gz 7ae9ec8aab2d8a811842d9dc67d8bf6c179e11ee bash_process-1.2.4.tar.gz 9e21f447bfa0431ae5097301d553dd6df3c58218 bash_process-1.2.tar.gz a65ce644b6094d41e9f991ef851cf05eff5dd0a9 bib2xhtml-20171221.tar.gz 4085dd6060a32d7e0d2e3f874c463a9964fd409b bib2xhtml-20190409.tar.gz f92cff635dfba5d4d77f469307369226c868542c cakeml-2.0.tar.gz e7ffe4238b61a3c1ee87aca4421e7a612e09b836 ci-extras-1.tar.gz e880f31f59bd403fb72fcd3b5afb413c3831a21c csdp-6.1-1.tar.gz 2659100ba8e28e7cb0ecb554178ee5315d4a87f5 csdp-6.1.1.tar.gz a2bd94f4f9281dc70dfda66cf28016c2ffef7ed7 csdp-6.1.tar.gz ec17080269737e4a97b4424a379924c09b338ca2 csdp-6.2.0.tar.gz 70105fd6fbfd1a868383fc510772b95234325d31 csdp-6.x.tar.gz 2f6417b8e96a0e4e8354fe0f1a253c18fb55d9a7 cvc3-2.4.1.tar.gz d70bfbe63590153c07709dea7084fbc39c669841 cvc4-1.5-1.tar.gz 541eac340464c5d34b70bb163ae277cc8829c40f cvc4-1.5-2.tar.gz 1a44895d2a440091a15cc92d7f77a06a2e432507 cvc4-1.5-3.tar.gz c0d8d5929b00e113752d8bf5d11241cd3bccafce cvc4-1.5-4.tar.gz ffb0d4739c10eb098eb092baef13eccf94a79bad cvc4-1.5-5.tar.gz 3682476dc5e915cf260764fa5b86f1ebdab57507 cvc4-1.5.tar.gz a5e02b5e990da4275dc5d4480c3b72fc73160c28 cvc4-1.5pre-1.tar.gz 4d9658fd2688ae8ac78da8fdfcbf85960f871b71 cvc4-1.5pre-2.tar.gz b01fdb93f2dc2b8bcfd41c6091d91b37d6e240f9 cvc4-1.5pre-3.tar.gz 76ff6103b8560f0e2778bbfbdb05f5fa18f850b7 cvc4-1.5pre-4.tar.gz 03aec2ec5757301c9df149f115d1f4f1d2cafd9e cvc4-1.5pre.tar.gz e99560d0b7cb9bafde2b0ec1a3a95af315918a25 cvc4-1.8.tar.gz 842d9526f37b928cf9e22f141884365129990d63 cygwin-20130110.tar.gz cb3b0706d208f104b800267697204f6d82f7b48a cygwin-20130114.tar.gz 3b44cca04855016d5f8cfb5101b2e0579ab80197 cygwin-20130117.tar.gz 1fde9ddf0fa4f398965113d0c0c4f0e97c78d008 cygwin-20130716.tar.gz a03735a53c2963eb0b453f6a7282d3419f28bf38 cygwin-20130916.tar.gz 7470125fc46e24ee188bdaacc6d560e01b6fa839 cygwin-20140520.tar.gz db4dedae026981c5f001be283180abc1962b79ad cygwin-20140521.tar.gz acbc4bf161ad21e96ecfe506266ccdbd288f8a6f cygwin-20140530.tar.gz 3dc680d9eb85276e8c3e9f6057dad0efe2d5aa41 cygwin-20140626.tar.gz 8e562dfe57a2f894f9461f4addedb88afa108152 cygwin-20140725.tar.gz 238d8e30e8e22495b7ea3f5ec36e852e97fe8bbf cygwin-20140813.tar.gz 629b8fbe35952d1551cd2a7ff08db697f6dff870 cygwin-20141024.tar.gz ce93d0b3b2743c4f4e5bba30c2889b3b7bc22f2c cygwin-20150410.tar.gz fa712dd5ec66ad16add1779d68aa171ff5694064 cygwin-20151210.tar.gz 056b843d5a3b69ecf8a52c06f2ce6e696dd275f9 cygwin-20151221.tar.gz 44f3a530f727e43a9413226c2423c9ca3e4c0cf5 cygwin-20161002.tar.gz dd56dd16d861fc6e1a008bf5e9da6f33ed6eb820 cygwin-20161022.tar.gz d9ad7aae99d54e3b9813151712eb88a441613f04 cygwin-20161024.tar.gz f8eb6a0f722e3cfe3775d1204c5c7063ee1f008e cygwin-20170828.tar.gz c22048912b010a5a0b4f2a3eb4d318d6953761e4 cygwin-20170930.tar.gz 5a3919e665947b820fd7f57787280c7512be3782 cygwin-20180604.tar.gz 2aa049170e8088de59bd70eed8220f552093932d cygwin-20190320.tar.gz fb898e263fcf6f847d97f564fe49ea0760bb453f cygwin-20190322.tar.gz cd01fac0ab4fdb50a2bbb6416da3f15a4d540da1 cygwin-20190524.tar.gz caa616fbab14c1fce790a87db5c4758c1322cf28 cygwin-20200116.tar.gz f053a9ab01f0be9cb456560f7eff66a8e7ba2fd2 cygwin-20200323.tar.gz 0107343cd2562618629f73b2581168f0045c3234 cygwin-20201002.tar.gz a3d481401b633c0ee6abf1da07d75da94076574c cygwin-20201130.tar.gz 0fe549949a025d65d52d6deca30554de8fca3b6e e-1.5.tar.gz 2e293256a134eb8e5b1a283361b15eb812fbfbf1 e-1.6-1.tar.gz e1919e72416cbd7ac8de5455caba8901acc7b44d e-1.6-2.tar.gz b98a98025d1f7e560ca6864a53296137dae736b4 e-1.6.tar.gz c11b25c919e2ec44fe2b6ac2086337b456344e97 e-1.8.tar.gz a895a96ec7e6fcc275114bb9b4c92b20fac73dba e-2.0-1.tar.gz 2ebd7e3067a2cdae3cb8b073345827013978d74b e-2.0-2.tar.gz fac44556dd16f666a2c186be30aa6d8c67228bb9 e-2.0-3.tar.gz 5d36fb62912cfcff7f3b99a6266c578aafc288b7 e-2.0-4.tar.gz 3223c51c0b16fe00ced4ae903041fff858e61742 e-2.0-5.tar.gz 6b962a6b4539b7ca4199977973c61a8c98a492e8 e-2.0.tar.gz 66449a7b68b7d85a7189e10735a81069356123b6 e-2.5-1.tar.gz 813b66ca151d7a39b5cacb39ab52acabc2a54845 e-2.5.tar.gz 6d34b18ca0aa1e10bab6413045d079188c0e2dfb exec_process-1.0.1.tar.gz 8b9bffd10e396d965e815418295f2ee2849bea75 exec_process-1.0.2.tar.gz e6aada354da11e533af2dee3dcdd96c06479b053 exec_process-1.0.3.tar.gz ae7ee5becb26512f18c609e83b34612918bae5f0 exec_process-1.0.tar.gz 7a4b46752aa60c1ee6c53a2c128dedc8255a4568 flatlaf-0.46-1.tar.gz ed5cbc216389b655dac21a19e770a02a96867b85 flatlaf-0.46.tar.gz d37b38b9a27a6541c644e22eeebe9a339282173d flatlaf-1.0-rc1.tar.gz dac46ce81cee10fb36a9d39b414dec7b7b671545 flatlaf-1.0-rc2.tar.gz d94e6da7299004890c04a7b395a3f2d381a3281e flatlaf-1.0-rc3.tar.gz 7ca3e6a8c9bd837990e64d89e7fa07a7e7cf78ff flatlaf-1.0.tar.gz 9908e5ab721f1c0035c0ab04dc7ad0bd00a8db27 flatlaf-1.2.tar.gz f339234ec18369679be0095264e0c0af7762f351 gnu-utils-20210414.tar.gz 683acd94761ef460cca1a628f650355370de5afb hol-light-bundle-0.5-126.tar.gz 989234b3799fe8750f3c24825d1f717c24fb0214 idea-icons-20210508.tar.gz 20b53cfc3ffc5b15c1eabc91846915b49b4c0367 isabelle_fonts-20151021.tar.gz 736844204b2ef83974cd9f0a215738b767958c41 isabelle_fonts-20151104.tar.gz 9502c1aea938021f154adadff254c5c55da344bd isabelle_fonts-20151106.tar.gz f5c63689a394b974ac0d365debda577c6fa31c07 isabelle_fonts-20151107.tar.gz 812101680b75f7fa9ee8e138ea6314fa4824ea2d isabelle_fonts-20151229.tar.gz 2730e1475c7d655655882e75743e0b451725a274 isabelle_fonts-20151231.tar.gz 1f004a6bf20088a7e8f1b3d4153aa85de6fc1091 isabelle_fonts-20160101.tar.gz 379d51ef3b71452dac34ba905def3daa8b590f2e isabelle_fonts-20160102.tar.gz 878536aab1eaf1a52da560c20bb41ab942971fa3 isabelle_fonts-20160227.tar.gz 8ff0eedf0191d808ecc58c6b3149a4697f29ab21 isabelle_fonts-20160812-1.tar.gz 9283e3b0b4c7239f57b18e076ec8bb21021832cb isabelle_fonts-20160812.tar.gz 620cffeb125e198b91a716da116f754d6cc8174b isabelle_fonts-20160830.tar.gz b70690c85c05d0ca5bc29287abd20142f6ddcfb0 isabelle_fonts-20171222.tar.gz c17c482e411bbaf992498041a3e1dea80336aaa6 isabelle_fonts-20171230.tar.gz 3affbb306baff37c360319b21cbaa2cc96ebb282 isabelle_fonts-20180113.tar.gz bee32019e5d7cf096ef2ea1d836c732e9a7628cc isabelle_fonts-20181124.tar.gz f249bc2c85bd2af9eee509de17187a766b74ab86 isabelle_fonts-20181129.tar.gz 928b5320073d04d93bcc5bc4347b6d01632b9d45 isabelle_fonts-20190210.tar.gz dfcdf9a757b9dc36cee87f82533b43c58ba84abe isabelle_fonts-20190309.tar.gz 95e3acf038df7fdeeacd8b4769930e6f57bf3692 isabelle_fonts-20190406.tar.gz dabcf5085d67c99159007007ff0e9bf775e423d1 isabelle_fonts-20190409.tar.gz 76827987c70051719e117138858930d42041f57d isabelle_fonts-20190717.tar.gz abc8aea3ae471f9313917008ac90e5c1c99e17da isabelle_fonts-20210317.tar.gz 3ff9195aab574fc75ca3b77af0adb33f9b6d7b74 isabelle_fonts-20210318.tar.gz b166b4bd583b6442a5d75eab06f7adbb66919d6d isabelle_fonts-20210319.tar.gz 9467ad54a9ac10a6e7e8db5458d8d2a5516eba96 isabelle_fonts-20210321.tar.gz 1f7a0b9829ecac6552b21e995ad0f0ac168634f3 isabelle_fonts-20210322.tar.gz 916adccd2f40c55116b68b92ce1eccb24d4dd9a2 isabelle_setup-20210630.tar.gz c611e363287fcc9bdd93c33bef85fa4e66cd3f37 isabelle_setup-20210701.tar.gz a0e7527448ef0f7ce164a38a50dc26e98de3cad6 isabelle_setup-20210709.tar.gz e413706694b0968245ee15183af2d464814ce0a4 isabelle_setup-20210711.tar.gz +d2c9fd7b73457a460111edd6eb93a133272935fb isabelle_setup-20210715.tar.gz 0b2206f914336dec4923dd0479d8cee4b904f544 jdk-11+28.tar.gz e12574d838ed55ef2845acf1152329572ab0cc56 jdk-11.0.10+9.tar.gz 3e05213cad47dbef52804fe329395db9b4e57f39 jdk-11.0.2+9.tar.gz 06ac8993b5bebd02c70f1bd18ce13075f01115f3 jdk-11.0.3+7.tar.gz e7e3cc9b0550c1e5d71197ad8c30f92b622d7183 jdk-11.0.4+11.tar.gz 49007a84a2643a204ce4406770dfd574b97880d9 jdk-11.0.5+10.tar.gz 3c250e98eb82f98afc6744ddc9170d293f0677e1 jdk-11.0.6+10.tar.gz 76cf7a141e15db30bd975089c65c833b58092aa7 jdk-11.0.9+11.tar.gz 71d19df63816e9be1c4c5eb44aea7a44cfadb319 jdk-11.tar.gz 72455a2fdb6cced9cd563f4d5d6134f7a6c34913 jdk-15.0.1+9.tar.gz e8ae300e61b0b121018456d50010b555bc96ce10 jdk-15.0.2+7.tar.gz 8d83e433c1419e0c0cc5fd1762903d11b4a5752c jdk-6u31.tar.gz 38d2d2a91c66714c18430e136e7e5191af3996e6 jdk-7u11.tar.gz d765bc4ad2f34d494429b2a8c1563c49db224944 jdk-7u13.tar.gz 13a265e4b706ece26fdfa6fc9f4a3dd1366016d2 jdk-7u21.tar.gz 5080274f8721a18111a7f614793afe6c88726739 jdk-7u25.tar.gz dd24d63afd6d17b29ec9cb2b2464d4ff2e02de2c jdk-7u40.tar.gz ec740ee9ffd43551ddf1e5b91641405116af6291 jdk-7u6.tar.gz 71b629b2ce83dbb69967c4785530afce1bec3809 jdk-7u60.tar.gz e119f4cbfa2a39a53b9578d165d0dc44b59527b7 jdk-7u65.tar.gz d6d1c42989433839fe64f34eb77298ef6627aed4 jdk-7u67.tar.gz b66039bc6dc2bdb2992133743005e1e4fc58ae24 jdk-7u72.tar.gz d980055694ddfae430ee001c7ee877d535e97252 jdk-7u76.tar.gz baa6de37bb6f7a104ce5fe6506bca3d2572d601a jdk-7u80.tar.gz 7d5b152ac70f720bb9e783fa45ecadcf95069584 jdk-7u9.tar.gz baf275a68d3f799a841932e4e9a95a1a604058ae jdk-8u102.tar.gz 5442f1015a0657259be0590b04572cd933431df7 jdk-8u11.tar.gz 741de6a4a805a0f9fb917d1845409e99346c2747 jdk-8u112.tar.gz ae7df8bd0c18eb40237cf54cc28933f4893b9c92 jdk-8u121.tar.gz 51531a3a0c16e180ed95cb7d2bd680c2ec0aa553 jdk-8u131.tar.gz e45edcf184f608d6f4a7b966d65a5d3289462693 jdk-8u144.tar.gz 264e806b9300a4fb3b6e15ba0e2c664d4ea698c8 jdk-8u152.tar.gz 84b04d877a2ea3a4e2082297b540e14f76722bc5 jdk-8u162.tar.gz 87303a0de3fd595aa3857c8f7cececa036d6ed18 jdk-8u172.tar.gz 9ae0338a5277d8749b4b4c7e65fc627319d98b27 jdk-8u181.tar.gz cfecb1383faaf027ffbabfcd77a0b6a6521e0969 jdk-8u20.tar.gz 44ffeeae219782d40ce6822b580e608e72fd4c76 jdk-8u31.tar.gz c95ebf7777beb3e7ef10c0cf3f734cb78f9828e4 jdk-8u5.tar.gz 4132cf52d5025bf330d53b96a5c6466fef432377 jdk-8u51.tar.gz 74df343671deba03be7caa49de217d78b693f817 jdk-8u60.tar.gz dfb087bd64c3e5da79430e0ba706b9abc559c090 jdk-8u66.tar.gz 2ac389babd15aa5ddd1a424c1509e1c459e6fbb1 jdk-8u72.tar.gz caa0cf65481b6207f66437576643f41dabae3c83 jdk-8u92.tar.gz +778fd85c827ec49d2d658a832d20e63916186b0d jedit-20210715.tar.gz 44775a22f42a9d665696bfb49e53c79371c394b0 jedit_build-20111217.tar.gz a242a688810f2bccf24587b0062ce8027bf77fa2 jedit_build-20120304.tar.gz 4c948dee53f74361c097c08f49a1a5ff9b17bd1d jedit_build-20120307.tar.gz 9c221fe71af8a063fcffcce21672a97aea0a8d5b jedit_build-20120313.tar.gz ed72630f307729df08fdedb095f0af8725f81b9c jedit_build-20120327.tar.gz 6425f622625024c1de27f3730d6811f6370a19cd jedit_build-20120414.tar.gz 7b012f725ec1cc102dc259df178d511cc7890bba jedit_build-20120813.tar.gz 8e1d36f5071e3def2cb281f7fefe9f52352cb88f jedit_build-20120903.tar.gz 8fa0c67f59beba369ab836562eed4e56382f672a jedit_build-20121201.tar.gz 06e9be2627ebb95c45a9bcfa025d2eeef086b408 jedit_build-20130104.tar.gz c85c0829b8170f25aa65ec6852f505ce2a50639b jedit_build-20130628.tar.gz 5de3e399be2507f684b49dfd13da45228214bbe4 jedit_build-20130905.tar.gz 87136818fd5528d97288f5b06bd30c787229eb0d jedit_build-20130910.tar.gz c63189cbe39eb8104235a0928f579d9523de78a9 jedit_build-20130925.tar.gz 65cc13054be20d3a60474d406797c32a976d7db7 jedit_build-20130926.tar.gz 30ca171f745adf12b65c798c660ac77f9c0f9b4b jedit_build-20131106.tar.gz 054c1300128f8abd0f46a3e92c756ccdb96ff2af jedit_build-20140405.tar.gz 4a963665537ea66c69de4d761846541ebdbf69f2 jedit_build-20140511.tar.gz a9d637a30f6a87a3583f265da51e63e3619cff52 jedit_build-20140722.tar.gz f29391c53d85715f8454e1aaa304fbccc352928f jedit_build-20141018.tar.gz d7206d4c9d14d3f4c8115422b7391ffbcc6e80b4 jedit_build-20141026.tar.gz f15d36abc1780875a46b6dbd4568e43b776d5db6 jedit_build-20141104.tar.gz 14ce124c897abfa23713928dc034d6ef0e1c5031 jedit_build-20150228.tar.gz b5f7115384c167559211768eb5fe98138864473b jedit_build-20151023.tar.gz 8ba7b6791be788f316427cdcd805daeaa6935190 jedit_build-20151124.tar.gz c70c5a6c565d435a09a8639f8afd3de360708e1c jedit_build-20160330.tar.gz d4e1496c257659cf15458d718f4663cdd95a404e jedit_build-20161024.tar.gz d806c1c26b571b5b4ef05ea11e8b9cf936518e06 jedit_build-20170319.tar.gz 7bcb202e13358dd750e964b2f747664428b5d8b3 jedit_build-20180417.tar.gz 23c8a05687d05a6937f7d600ac3aa19e3ce59c9c jedit_build-20180504.tar.gz 9c64ee0705e5284b507ca527196081979d689519 jedit_build-20181025.tar.gz cfa65bf8720b9b798ffa0986bafbc8437f44f758 jedit_build-20181026.tar.gz 847492b75b38468268f9ea424d27d53f2d95cef4 jedit_build-20181203.tar.gz 536a38ed527115b4bf2545a2137ec57b6ffad718 jedit_build-20190120.tar.gz 58b9f03e5ec0b85f8123c31f5d8092dae5803773 jedit_build-20190130.tar.gz ec0aded5f2655e2de8bc4427106729e797584f2f jedit_build-20190224.tar.gz 1e53598a02ec8d8736b15f480cbe2c84767a7827 jedit_build-20190508.tar.gz b9c6f49d3f6ebe2e85a50595ce7412d01a4314ac jedit_build-20190717.tar.gz 1c753beb93e92e95e99e8ead23a68346bd1af44a jedit_build-20200610.tar.gz 533b1ee6459f59bcbe4f09e214ad2cb990fb6952 jedit_build-20200908.tar.gz f9966b5ed26740bb5b8bddbfe947fcefaea43d4d jedit_build-20201223.tar.gz 0bdbd36eda5992396e9c6b66aa24259d4dd7559c jedit_build-20210201.tar.gz a0744f1948abdde4bfb51dd4769b619e7444baf1 jedit_build-20210510-1.tar.gz 837d6c8f72ecb21ad59a2544c69aadc9f05684c6 jedit_build-20210510.tar.gz 7bdae3d24b10261f6cb277446cf9ecab6062bd6f jedit_build-20210708.tar.gz 0bd2bc2d9a491ba5fc8dd99df27c04f11a72e8fa jfreechart-1.0.14-1.tar.gz 8122526f1fc362ddae1a328bdbc2152853186fee jfreechart-1.0.14.tar.gz d911f63a5c9b4c7335bb73f805cb1711ce017a84 jfreechart-1.5.0.tar.gz d84b7d8ef273afec55284327fca7dd20f5ecb77a jfreechart-1.5.1.tar.gz c8a19a36adf6cefa779d85f22ded2f4654e68ea5 jortho-1.0-1.tar.gz 2155e0bdbd29cd3d2905454de2e7203b9661d239 jortho-1.0-2.tar.gz ffe179867cf5ffaabbb6bb096db9bdc0d7110065 jortho-1.0.tar.gz 6c737137cc597fc920943783382e928ea79e3feb kodkodi-1.2.16.tar.gz afb04f4048a87bb888fe7b05b0139cb060c7925b kodkodi-1.5.2-1.tar.gz 5f95c96bb99927f3a026050f85bd056f37a9189e kodkodi-1.5.2.tar.gz 0634a946b216f7f07f1a0f7e28cf345daa28828f kodkodi-1.5.3.tar.gz 52e95b3493d71902f9df89d0bb59d0046a5f0c63 kodkodi-1.5.4-1.tar.gz 267189c637de26cf304d699cfa95389da002b250 kodkodi-1.5.4.tar.gz 3ecdade953bb455ed2907952be287d7e5cf6533b kodkodi-1.5.5.tar.gz 8aa939f5127290eb9a99952d375be9ffbf90c43b kodkodi-1.5.6-1.tar.gz 6b12bf3f40b16fae8ff22aa39171fa018d107cb3 kodkodi-1.5.6.tar.gz 377e36efb8608e6c828c7718d890e97fde2006a4 linux_app-20131007.tar.gz 759848095e2ad506083d92b5646947e3c32f27a0 linux_app-20191223.tar.gz 1a449ce69ac874e21804595d16aaaf5a0d0d0c10 linux_app-20200110.tar.gz 0aab4f73ff7f5e36f33276547e10897e1e56fb1d macos_app-20130716.tar.gz ad5d0e640ce3609a885cecab645389a2204e03bb macos_app-20150916.tar.gz 400af57ec5cd51f96928d9de00d077524a6fe316 macos_app-20181205.tar.gz 3bc42b8e22f0be5ec5614f1914066164c83498f8 macos_app-20181208.tar.gz 0fbc826e4fcb95bb9e1814642f7fce788e7fe1c3 naproche-20210122-1.tar.gz eda10c62da927a842c0a8881f726eac85e1cb4f7 naproche-20210122.tar.gz edcb517b7578db4eec1b6573b624f291776e11f6 naproche-20210124.tar.gz d858eb0ede6aea6b8cc40de63bd3a17f8f9f5300 naproche-20210129.tar.gz 810ee0f35adada9bf970c33fd80b986ab2255bf3 naproche-20210201.tar.gz 4a4e56fd03b7ba4edd38046f853873a90cf55d1a naproche-4ad61140062f.tar.gz 77252e0b40f89825b9b5935f9f0c4cd5d4e7012a naproche-6d0d76ce2f2a.tar.gz 9c02ecf93863c3289002c5e5ac45a83e2505984c naproche-755224402e36.tar.gz e1b34e8f54e7e5844873612635444fed434718a1 naproche-7d0947a91dd5.tar.gz 26df569cee9c2fd91b9ac06714afd43f3b37a1dd nunchaku-0.3.tar.gz e573f2cbb57eb7b813ed5908753cfe2cb41033ca nunchaku-0.5.tar.gz fe57793aca175336deea4f5e9c0d949a197850ac opam-1.2.2.tar.gz eb499a18e7040ca0fe1ca824c9dcb2087c47c9ba opam-2.0.3-1.tar.gz 002f74c9e65e650de2638bf54d7b012b8de76c28 opam-2.0.3.tar.gz ddb3b438430d9565adbf5e3d913bd52af8337511 opam-2.0.6.tar.gz fc66802c169f44511d3be30435eb89a11e635742 opam-2.0.7.tar.gz 1c8cb6a8f4cbeaedce2d6d1ba8fc7e2ab3663aeb polyml-5.4.1.tar.gz a3f9c159a0ee9a63b7a5d0c835ed9c2c908f8b56 polyml-5.5.0-1.tar.gz 7d604a99355efbfc1459d80db3279ffa7ade3e39 polyml-5.5.0-2.tar.gz b3d776e6744f0cd2773d467bc2cfe1de3d1ca2fd polyml-5.5.0-3.tar.gz 1812e9fa6d163f63edb93e37d1217640a166cf3e polyml-5.5.0.tar.gz 36f5b8224f484721749682a3655c796a55a2718d polyml-5.5.1-1.tar.gz 36f78f27291a9ceb13bf1120b62a45625afd44a6 polyml-5.5.1.tar.gz a588640dbf5da9ae15455b02ef709764a48637dc polyml-5.5.2-1.tar.gz 4b690390946f7bfb777b89eb16d6f08987cca12f polyml-5.5.2-2.tar.gz 5b31ad8556e41dfd6d5e85f407818be399aa3d2a polyml-5.5.2-3.tar.gz 532f6e8814752aeb406c62fabcfd2cc05f8a7ca8 polyml-5.5.2.tar.gz 1c53f699d35c0db6c7cf4ea51f2310adbd1d0dc5 polyml-5.5.3-20150820.tar.gz b4b624fb5f34d1dc814fb4fb469fafd7d7ea018a polyml-5.5.3-20150908.tar.gz b668e1f43a41608a8eb365c5e19db6c54c72748a polyml-5.5.3-20150911.tar.gz 1f5cd9b1390dab13861f90dfc06d4180cc107587 polyml-5.5.3-20150916.tar.gz f78896e588e8ebb4da57bf0c95210b0f0fa9e551 polyml-5.6-1.tar.gz 21fa0592b7dfd23269063f42604438165630c0f0 polyml-5.6-2.tar.gz 03ba81e595fa6d6df069532d67ad3195c37d9046 polyml-5.6-20151123.tar.gz 822f489c18e38ce5ef979ec21dccce4473e09be6 polyml-5.6-20151206.tar.gz bd6a448f0e0d5787747f4f30ca661f9c1868e4a7 polyml-5.6-20151223.tar.gz 5b70c12c95a90d858f90c1945011289944ea8e17 polyml-5.6-20160118.tar.gz 5b19dc93082803b82aa553a5cfb3e914606c0ffd polyml-5.6.tar.gz 80b923fca3533bf291ff9da991f2262a98b68cc4 polyml-5.7-20170217.tar.gz 381a70cecf0fdee47f6842e2bdb5107ed52adab6 polyml-5.7.1-1.tar.gz 39dac33b569ac66f76126b8f4edc6d9227bd8a63 polyml-5.7.1-2.tar.gz 0b896ccc35bd3f2541cd55e6f0ed14637ed9fc68 polyml-5.7.1-4.tar.gz 262450ac9966abebae2e1d4f9ae703cfe0f5d8d9 polyml-5.7.1-5.tar.gz 1aeb57877d694db7fe4d4395287cddf3bc77710b polyml-5.7.1-6.tar.gz e3e7e20b1e0e5d5d68df4cd4caa1e1a7410d46b6 polyml-5.7.1-7.tar.gz 1430533c09b17f8be73798a47a5f409d43a04cf4 polyml-5.7.1-8.tar.gz 171b5783b88522a35e4822b19ef8ba838c04f494 polyml-5.7.1.tar.gz 5fbcab1da2b5eb97f24da2590ece189d55b3a105 polyml-5.7.tar.gz 51e024225b460900da5279f0b91b217085f98cf9 polyml-5.8-20190220.tar.gz 20a83fa58d497b533150defe39bcd4540529b25f polyml-5.8-20190306.tar.gz 9f0e9cd10df4c3383b063eb076e8b698ca50c3d0 polyml-5.8.1-20191101.tar.gz f46deb909d645ac8c140968e4d32b5763beb9add polyml-5.8.1-20191113.tar.gz 36a40a981b57daae0463d14940a8edf6fa1af179 polyml-5.8.1-20191114.tar.gz 525b05536b08c11a1eae943fe6818a8622326084 polyml-5.8.1-20191124.tar.gz 9043828803483ca14df64488dff014ad050a6d34 polyml-5.8.1-20200228.tar.gz 1186607e2c43b77db86731f12fbedb531ca50a21 polyml-5.8.1-20200708.tar.gz 22ae16bf7850e73b903d2ca8eb506da05b441cf3 polyml-5.8.1.tar.gz cb8e85387315f62dcfc6b21ec378186e58068f76 polyml-5.8.2.tar.gz d1fd6eced69dc1df7226432fcb824568e0994ff2 polyml-5.8.tar.gz 49f1adfacdd6d29fa9f72035d94a31eaac411a97 polyml-test-0a6ebca445fc.tar.gz 2a8c4421e0a03c0d6ad556b3c36c34eb11568adb polyml-test-1236652ebd55.tar.gz 8e83fb5088cf265902b8da753a8eac5fe3f6a14b polyml-test-159dc81efc3b.tar.gz a0064c157a59e2706e18512a49a6dca914fa17fc polyml-test-1b2dcf8f5202.tar.gz 4e6543dbbb2b2aa402fd61428e1c045c48f18b47 polyml-test-79534495ee94.tar.gz 853ab0e9ff2b73790cc80a2d36cbff8b03e50a8e polyml-test-7a7b742897e9.tar.gz 85bfda83d138e936fdafd68ed3627b1058e5c2c3 polyml-test-7e49fce62e3d.tar.gz c629cd499a724bbe37b962f727e4ff340c50299d polyml-test-8529546198aa.tar.gz 7df4857d73dbc9edda25a6ad329e47639e70fadf polyml-test-8fda4fd22441.tar.gz 2b7c02b67feb2f44dda6938a7244f4257e7c580c polyml-test-905dae2ebfda.tar.gz 3dfdc58e5d9b28f038a725e05c9c2f2ce0bb2632 polyml-test-a3cfdf648da-1.tar.gz e2f075b0cc709f4f7f6492b725362f9010b2c6d1 polyml-test-a3cfdf648da-2.tar.gz 33568f69ce813b7405386ddbefa14ad0342bb8f0 polyml-test-a3cfdf648da.tar.gz 4bedaac4f1fb9a9199aa63695735063c47059003 polyml-test-a444f281ccec.tar.gz f3031692edcc5d8028a42861e4e40779f0f9d3e1 polyml-test-b68438d33c69.tar.gz cb2318cff6ea9293cd16a4435a4fe28ad9dbe0b8 polyml-test-cf46747fee61.tar.gz 67ffed2f98864721bdb1e87f0ef250e4c69e6160 polyml-test-d68c6736402e.tar.gz b4ceeaac47f3baae41c2491a8368b03217946166 polyml-test-e7a662f8f9c4.tar.gz 609c7d09d3ed01156ff91261e801e2403ff93729 polyml-test-e8d82343b692.tar.gz b6d87466e9b44e8ef4a2fac74c96b139080a506a polyml-test-f54aa41240d0.tar.gz d365f3fc11c2427cafc62b3c79951880a1476ebb polyml-test-f86ae3dc1686.tar.gz a619177143fea42a464f49bb864665407c07a16c polyml-test-fb4f42af00fa.tar.gz 53123dc011b2d4b4e8fe307f3c9fa355718ad01a postgresql-42.1.1.tar.gz 3a5d31377ec07a5069957f5477a4848cfc89a594 postgresql-42.1.4.tar.gz 7d6ef4320d5163ceb052eb83c1cb3968f099a422 postgresql-42.2.18.tar.gz e7cd5c7955e9eb5ce8cd07feb97230b23d2eec40 postgresql-42.2.2.tar.gz 231b33c9c3c27d47e3ba01b399103d70509e0731 postgresql-42.2.5.tar.gz 6335fbc0658e447b5b9bc48c9ad36e33a05bb72b postgresql-42.2.9.tar.gz f132329ca1045858ef456cc08b197c9eeea6881b postgresql-9.4.1212.tar.gz 0885e1f1d8feaca78d2f204b6487e6eec6dfab4b scala-2.10.0.tar.gz f7dc7a4e1aea46408fd6e44b8cfacb33af61afbc scala-2.10.1.tar.gz 207e4916336335386589c918c5e3f3dcc14698f2 scala-2.10.2.tar.gz 21c8ee274ffa471ab54d4196ecd827bf3d43e591 scala-2.10.3.tar.gz d4688ddaf83037ca43b5bf271325fc53ae70e3aa scala-2.10.4.tar.gz 44d12297a78988ffd34363535e6a8e0d94c1d8b5 scala-2.11.0.tar.gz 14f20de82b25215a5e055631fb147356400625e6 scala-2.11.1.tar.gz 4fe9590d08e55760b86755d3fab750e90ac6c380 scala-2.11.2.tar.gz 27a296495b2167148de06314ed9a942f2dbe23fe scala-2.11.4.tar.gz 4b24326541161ce65424293ca9da3e7c2c6ab452 scala-2.11.5.tar.gz e7cf20e3b27c894c6127c7a37042c1667f57385e scala-2.11.6.tar.gz 4810c1b00719115df235be1c5991aa6ea7186134 scala-2.11.7.tar.gz 3eca4b80710996fff87ed1340dcea2c5f6ebf4f7 scala-2.11.8.tar.gz 0004e53f885fb165b50c95686dec40d99ab0bdbd scala-2.12.0.tar.gz 059cbdc58d36e3ac1fffcccd9139ecd34f271882 scala-2.12.10.tar.gz 82056106aa6fd37c159ea76d16096c20a749cccd scala-2.12.11.tar.gz fe7ff585acffaad7f0dd4a1d079134d15c26ed0d scala-2.12.12.tar.gz 74a8c3dab3a25a87357996ab3e95d825dc820fd0 scala-2.12.2.tar.gz d66796a68ec3254b46b17b1f8ee5bcc56a93aacf scala-2.12.3.tar.gz 1636556167dff2c191baf502c23f12e09181ef78 scala-2.12.4.tar.gz 8171f494bba54fb0d01c887f889ab8fde7171c2a scala-2.12.5.tar.gz 54c1b06fa2c5f6c2ab3d391ef342c0532cd7f392 scala-2.12.6.tar.gz 02358f00acc138371324b6248fdb62eed791c6bd scala-2.12.7.tar.gz 201c05ae9cc382ee6c08af49430e426f6bbe0d5a scala-2.12.8.tar.gz a0622fe75c3482ba7dc3ce74d58583b648a1ff0d scala-2.13.4-1.tar.gz ec53cce3c5edda1145ec5d13924a5f9418995c15 scala-2.13.4.tar.gz f51981baf34c020ad103b262f81796c37abcaa4a scala-2.13.5.tar.gz 0a7cab09dec357dab7819273f2542ff1c3ea0968 scala-2.13.6.tar.gz b447017e81600cc5e30dd61b5d4962f6da01aa80 scala-2.8.1.final.tar.gz 5659440f6b86db29f0c9c0de7249b7e24a647126 scala-2.9.2.tar.gz abe7a3b50da529d557a478e9f631a22429418a67 smbc-0.4.1.tar.gz cbd491c0feba1d21019d05564e76dd04f592ccb4 spass-3.8ds-1.tar.gz edaa1268d82203067657aabcf0371ce7d4b579b9 spass-3.8ds-2.tar.gz 43b5afbcad575ab6817d2289756ca22fd2ef43a9 spass-3.8ds.tar.gz b016a785f1f78855c00d351ff598355c3b87450f sqlite-jdbc-3.18.0-1.tar.gz b85b5bc071a59ef2a8326ceb1617d5a9a5be41cf sqlite-jdbc-3.18.0.tar.gz e56117a67ab01fb24c7fc054ede3160cefdac5f8 sqlite-jdbc-3.20.0.tar.gz 27aeac6a91353d69f0438837798ac4ae6f9ff8c5 sqlite-jdbc-3.23.1.tar.gz 4d17611857fa3a93944c1f159c0fd2a161967aaf sqlite-jdbc-3.27.2.1.tar.gz 806be457eb79408fcc5a72aeca3f64b2d89a6b63 sqlite-jdbc-3.30.1.tar.gz cba2b194114216b226d75d49a70d1bd12b141ac8 sqlite-jdbc-3.32.3.2.tar.gz 29306acd6ce9f4c87032b2c271c6df035fe7d4d3 sqlite-jdbc-3.34.0.tar.gz 8d20968603f45a2c640081df1ace6a8b0527452a sqlite-jdbc-3.8.11.2.tar.gz 2369f06e8d095f9ba26df938b1a96000e535afff ssh-java-20161009.tar.gz a2335d28b5b95d8d26500a53f1a9303fc5beaf36 ssh-java-20190323.tar.gz fdc415284e031ee3eb2f65828cbc6945736fe995 stack-1.9.1.tar.gz 6e19948ff4a821e2052fc9b3ddd9ae343f4fcdbb stack-1.9.3.tar.gz f969443705aa8619e93af5b34ea98d15cd7efaf1 stack-2.1.3.tar.gz ebd0221d038966aa8bde075f1b0189ff867b02ca stack-2.5.1.tar.gz 1f4a2053cc1f34fa36c4d9d2ac906ad4ebc863fd sumatra_pdf-2.1.1.tar.gz 601e08d048d8e50b0729429c8928b667d9b6bde9 sumatra_pdf-2.3.2.tar.gz 14d46c2eb1a34821703da59d543433f581e91df3 sumatra_pdf-2.4.tar.gz 44d67b6742919ce59a42368fc60e2afa210a3e42 sumatra_pdf-2.5.2.tar.gz 89719a13bc92810730a430973684629426ed1b2a sumatra_pdf-3.0.tar.gz f5afcc82f8e734665d38867e99475d3ad0d5ed15 sumatra_pdf-3.1.1.tar.gz a45eca5c1277f42f87bb8dc12a3074ccf5488221 sumatra_pdf-3.1.2-1.tar.gz 3b3239b2e6f8062b90d819f3703e30a50f4fa1e7 sumatra_pdf-3.1.2-2.tar.gz 8486387f61557147ec06b1f637117c017c8f0528 sumatra_pdf-3.1.2.tar.gz 869ea6d8ea35c8ba68d7fcb028f16b2b7064c5fd vampire-1.0.tar.gz 399f687b56575b93e730f68c91c989cb48aa34d8 vampire-4.2.2.tar.gz 98c5c79fef7256db9f64c8feea2edef0a789ce46 verit-2016post.tar.gz 52ba18a6c96b53c5ae9b179d5a805a0c08f1da6d verit-2020.10-rmx-1.tar.gz b6706e74e20e14038e9b38f0acdb5639a134246a verit-2020.10-rmx.tar.gz 81d21dfd0ea5c58f375301f5166be9dbf8921a7a windows_app-20130716.tar.gz fe15e1079cf5ad86f3cbab4553722a0d20002d11 windows_app-20130905.tar.gz e6a43b7b3b21295853bd2a63b27ea20bd6102f5f windows_app-20130906.tar.gz 8fe004aead867d4c82425afac481142bd3f01fb0 windows_app-20130908.tar.gz d273abdc7387462f77a127fa43095eed78332b5c windows_app-20130909.tar.gz c368908584e2bca38b3bcb20431d0c69399fc2f0 windows_app-20131130.tar.gz c3f5285481a95fde3c1961595b4dd0311ee7ac1f windows_app-20131201.tar.gz 14807afcf69e50d49663d5b48f4b103f30ae842b windows_app-20150821.tar.gz ed106181510e825bf959025d8e0a2fc3f78e7a3f windows_app-20180417.tar.gz e809e4ab0d33cb413a7c47dd947e7dbdfcca1c24 windows_app-20181002.tar.gz 9e96ba128a0617a9020a178781df49d48c997e19 windows_app-20181006.tar.gz 1c36a840320dfa9bac8af25fc289a4df5ea3eccb xz-java-1.2-1.tar.gz 2ae13aa17d0dc95ce254a52f1dba10929763a10d xz-java-1.2.tar.gz c22196148fcace5443a933238216cff5112948df xz-java-1.5.tar.gz 4368ee09154dff42666a8c87e072261745619e51 xz-java-1.6.tar.gz 63f5fa09e92a895cb9aea27d7142abc86c487d25 xz-java-1.8.tar.gz 4530a1aa6f4498ee3d78d6000fa71a3f63bd077f yices-1.0.28.tar.gz 3a8f77822278fe9250890e357248bc678d8fac95 z3-3.2-1.tar.gz 12ae71acde43bd7bed1e005c43034b208c0cba4c z3-3.2.tar.gz d94a716502c8503d63952bcb4d4176fac8b28704 z3-4.0.tar.gz 86e721296c400ada440e4a9ce11b9e845eec9e25 z3-4.3.0.tar.gz a8917c31b31c182edeec0aaa48870844960c8a61 z3-4.3.2pre-1.tar.gz 06b30757ff23aefbc30479785c212685ffd39f4d z3-4.3.2pre.tar.gz 93e7e4bddc6afcf87fe2b6656cfcb1b1acd0a4f8 z3-4.4.0pre-1.tar.gz b1bc411c2083fc01577070b56b94514676f53854 z3-4.4.0pre-2.tar.gz 4c366ab255d2e9343fb635d44d4d55ddd24c76d0 z3-4.4.0pre-3.tar.gz 517ba7b94c1985416c5b411c8ae84456367eb231 z3-4.4.0pre.tar.gz aa20745f0b03e606b1a4149598e0c7572b63c657 z3-4.8.3.tar.gz 9dfeb39c87393af7b6a34118507637aa53aca05e zipperposition-2.0-1.tar.gz b884c60653002a7811e3b652ae0515e825d98667 zipperposition-2.0.tar.gz diff --git a/Admin/components/main b/Admin/components/main --- a/Admin/components/main +++ b/Admin/components/main @@ -1,31 +1,31 @@ #main components for repository clones or release bundles gnu-utils-20210414 bash_process-1.2.4-2 bib2xhtml-20190409 csdp-6.1.1 cvc4-1.8 e-2.5-1 flatlaf-1.2 idea-icons-20210508 isabelle_fonts-20210322 -isabelle_setup-20210711 +isabelle_setup-20210715 jdk-15.0.2+7 -jedit_build-20210708 +jedit-20210715 jfreechart-1.5.1 jortho-1.0-2 kodkodi-1.5.6-1 nunchaku-0.5 opam-2.0.7 polyml-5.8.2 postgresql-42.2.18 scala-2.13.5 smbc-0.4.1 spass-3.8ds-2 sqlite-jdbc-3.34.0 ssh-java-20190323 stack-2.5.1 vampire-4.2.2 verit-2020.10-rmx-1 xz-java-1.8 z3-4.4.0pre-3 zipperposition-2.0-1 diff --git a/Admin/etc/build.props b/Admin/etc/build.props new file mode 100644 --- /dev/null +++ b/Admin/etc/build.props @@ -0,0 +1,4 @@ +description = Isabelle/Scala/Admin +lib = $ISABELLE_HOME/lib/classes +name = isabelle_admin +services = isabelle.Admin_Tools diff --git a/bin/isabelle_java b/bin/isabelle_java --- a/bin/isabelle_java +++ b/bin/isabelle_java @@ -1,74 +1,72 @@ #!/usr/bin/env bash # # Author: Makarius # # Isabelle/Java cold start -- without settings environment unset CDPATH if [ -L "$0" ]; then TARGET="$(LC_ALL=C ls -l "$0" | sed 's/.* -> //')" exec "$(cd "$(dirname "$0")"; cd "$(pwd -P)"; cd "$(dirname "$TARGET")"; pwd)/$(basename "$TARGET")" "$@" fi export ISABELLE_HOME="$(cd "$(dirname "$0")"; cd "$(pwd -P)"; cd ..; pwd)" ( source "$ISABELLE_HOME/lib/scripts/getsettings" || exit 2 eval "declare -a JAVA_ARGS=($ISABELLE_JAVA_SYSTEM_OPTIONS $ISABELLE_TOOL_JAVA_OPTIONS)" - if [ -f "$ISABELLE_HOME/src/Tools/jEdit/dist/jedit.jar" ]; then - classpath "$ISABELLE_HOME/src/Tools/jEdit/dist/jedit.jar" - fi + isabelle_setup_classpath [ -n "$CLASSPATH" ] && classpath "$CLASSPATH" echo "$ISABELLE_ROOT" echo "$CYGWIN_ROOT" echo "$JAVA_HOME" echo "$(platform_path "$ISABELLE_CLASSPATH")" for ARG in "${JAVA_ARGS[@]}"; do echo "$ARG"; done ) | { LINE_COUNT=0 export ISABELLE_ROOT="" export CYGWIN_ROOT="" unset JAVA_HOME unset ISABELLE_CLASSPATH unset JAVA_ARGS; declare -a JAVA_ARGS while { unset REPLY; read -r; test "$?" = 0 -o -n "$REPLY"; } do case "$LINE_COUNT" in 0) LINE_COUNT=1 ISABELLE_ROOT="$REPLY" ;; 1) LINE_COUNT=2 CYGWIN_ROOT="$REPLY" ;; 2) LINE_COUNT=3 JAVA_HOME="$REPLY" ;; 3) LINE_COUNT=4 ISABELLE_CLASSPATH="$REPLY" ;; *) JAVA_ARGS["${#JAVA_ARGS[@]}"]="$REPLY" ;; esac done if [ -z "$JAVA_HOME" ]; then echo "Unknown JAVA_HOME -- Java unavailable" >&2 exit 127 else unset ISABELLE_HOME unset CLASSPATH exec "$JAVA_HOME/bin/java" "${JAVA_ARGS[@]}" \ -classpath "$ISABELLE_CLASSPATH" "$@" fi } diff --git a/etc/build.props b/etc/build.props new file mode 100644 --- /dev/null +++ b/etc/build.props @@ -0,0 +1,292 @@ +description = Isabelle/Scala +lib = lib/classes +name = isabelle +main = isabelle.jedit.Main +resources = \ + lib/services/java.nio.charset.spi.CharsetProvider:META-INF/services/ \ + lib/logo/isabelle_transparent-32.gif:isabelle/ \ + lib/logo/isabelle_transparent.gif:isabelle/ +sources = \ + src/HOL/SPARK/Tools/spark.scala \ + src/HOL/Tools/ATP/system_on_tptp.scala \ + src/HOL/Tools/Mirabelle/mirabelle.scala \ + src/HOL/Tools/Nitpick/kodkod.scala \ + src/Pure/Admin/afp.scala \ + src/Pure/Admin/build_csdp.scala \ + src/Pure/Admin/build_cygwin.scala \ + src/Pure/Admin/build_doc.scala \ + src/Pure/Admin/build_e.scala \ + src/Pure/Admin/build_fonts.scala \ + src/Pure/Admin/build_history.scala \ + src/Pure/Admin/build_jcef.scala \ + src/Pure/Admin/build_jdk.scala \ + src/Pure/Admin/build_jedit.scala \ + src/Pure/Admin/build_log.scala \ + src/Pure/Admin/build_polyml.scala \ + src/Pure/Admin/build_release.scala \ + src/Pure/Admin/build_spass.scala \ + src/Pure/Admin/build_sqlite.scala \ + src/Pure/Admin/build_status.scala \ + src/Pure/Admin/build_vampire.scala \ + src/Pure/Admin/build_verit.scala \ + src/Pure/Admin/build_zipperposition.scala \ + src/Pure/Admin/check_sources.scala \ + src/Pure/Admin/ci_profile.scala \ + src/Pure/Admin/isabelle_cronjob.scala \ + src/Pure/Admin/isabelle_devel.scala \ + src/Pure/Admin/jenkins.scala \ + src/Pure/Admin/other_isabelle.scala \ + src/Pure/Concurrent/consumer_thread.scala \ + src/Pure/Concurrent/counter.scala \ + src/Pure/Concurrent/delay.scala \ + src/Pure/Concurrent/event_timer.scala \ + src/Pure/Concurrent/future.scala \ + src/Pure/Concurrent/isabelle_thread.scala \ + src/Pure/Concurrent/mailbox.scala \ + src/Pure/Concurrent/par_list.scala \ + src/Pure/Concurrent/synchronized.scala \ + src/Pure/GUI/color_value.scala \ + src/Pure/GUI/desktop_app.scala \ + src/Pure/GUI/gui.scala \ + src/Pure/GUI/gui_thread.scala \ + src/Pure/GUI/popup.scala \ + src/Pure/GUI/wrap_panel.scala \ + src/Pure/General/antiquote.scala \ + src/Pure/General/bytes.scala \ + src/Pure/General/cache.scala \ + src/Pure/General/codepoint.scala \ + src/Pure/General/comment.scala \ + src/Pure/General/completion.scala \ + src/Pure/General/csv.scala \ + src/Pure/General/date.scala \ + src/Pure/General/exn.scala \ + src/Pure/General/file.scala \ + src/Pure/General/file_watcher.scala \ + src/Pure/General/graph.scala \ + src/Pure/General/graph_display.scala \ + src/Pure/General/graphics_file.scala \ + src/Pure/General/http.scala \ + src/Pure/General/json.scala \ + src/Pure/General/linear_set.scala \ + src/Pure/General/logger.scala \ + src/Pure/General/long_name.scala \ + src/Pure/General/mailman.scala \ + src/Pure/General/mercurial.scala \ + src/Pure/General/multi_map.scala \ + src/Pure/General/output.scala \ + src/Pure/General/path.scala \ + src/Pure/General/position.scala \ + src/Pure/General/pretty.scala \ + src/Pure/General/properties.scala \ + src/Pure/General/rdf.scala \ + src/Pure/General/scan.scala \ + src/Pure/General/sha1.scala \ + src/Pure/General/sql.scala \ + src/Pure/General/ssh.scala \ + src/Pure/General/symbol.scala \ + src/Pure/General/time.scala \ + src/Pure/General/timing.scala \ + src/Pure/General/untyped.scala \ + src/Pure/General/url.scala \ + src/Pure/General/utf8.scala \ + src/Pure/General/uuid.scala \ + src/Pure/General/value.scala \ + src/Pure/General/word.scala \ + src/Pure/General/xz.scala \ + src/Pure/Isar/document_structure.scala \ + src/Pure/Isar/keyword.scala \ + src/Pure/Isar/line_structure.scala \ + src/Pure/Isar/outer_syntax.scala \ + src/Pure/Isar/parse.scala \ + src/Pure/Isar/token.scala \ + src/Pure/ML/ml_console.scala \ + src/Pure/ML/ml_lex.scala \ + src/Pure/ML/ml_process.scala \ + src/Pure/ML/ml_profiling.scala \ + src/Pure/ML/ml_statistics.scala \ + src/Pure/ML/ml_syntax.scala \ + src/Pure/PIDE/byte_message.scala \ + src/Pure/PIDE/command.scala \ + src/Pure/PIDE/command_span.scala \ + src/Pure/PIDE/document.scala \ + src/Pure/PIDE/document_id.scala \ + src/Pure/PIDE/document_status.scala \ + src/Pure/PIDE/editor.scala \ + src/Pure/PIDE/headless.scala \ + src/Pure/PIDE/line.scala \ + src/Pure/PIDE/markup.scala \ + src/Pure/PIDE/markup_tree.scala \ + src/Pure/PIDE/protocol.scala \ + src/Pure/PIDE/protocol_handlers.scala \ + src/Pure/PIDE/protocol_message.scala \ + src/Pure/PIDE/prover.scala \ + src/Pure/PIDE/query_operation.scala \ + src/Pure/PIDE/rendering.scala \ + src/Pure/PIDE/resources.scala \ + src/Pure/PIDE/session.scala \ + src/Pure/PIDE/text.scala \ + src/Pure/PIDE/xml.scala \ + src/Pure/PIDE/yxml.scala \ + src/Pure/ROOT.scala \ + src/Pure/System/bash.scala \ + src/Pure/System/command_line.scala \ + src/Pure/System/components.scala \ + src/Pure/System/executable.scala \ + src/Pure/System/getopts.scala \ + src/Pure/System/isabelle_charset.scala \ + src/Pure/System/isabelle_fonts.scala \ + src/Pure/System/isabelle_platform.scala \ + src/Pure/System/isabelle_process.scala \ + src/Pure/System/isabelle_system.scala \ + src/Pure/System/isabelle_tool.scala \ + src/Pure/System/java_statistics.scala \ + src/Pure/System/linux.scala \ + src/Pure/System/mingw.scala \ + src/Pure/System/numa.scala \ + src/Pure/System/options.scala \ + src/Pure/System/platform.scala \ + src/Pure/System/posix_interrupt.scala \ + src/Pure/System/process_result.scala \ + src/Pure/System/progress.scala \ + src/Pure/System/scala.scala \ + src/Pure/System/system_channel.scala \ + src/Pure/System/tty_loop.scala \ + src/Pure/Thy/bibtex.scala \ + src/Pure/Thy/document_build.scala \ + src/Pure/Thy/export.scala \ + src/Pure/Thy/export_theory.scala \ + src/Pure/Thy/file_format.scala \ + src/Pure/Thy/html.scala \ + src/Pure/Thy/latex.scala \ + src/Pure/Thy/presentation.scala \ + src/Pure/Thy/sessions.scala \ + src/Pure/Thy/thy_element.scala \ + src/Pure/Thy/thy_header.scala \ + src/Pure/Thy/thy_syntax.scala \ + src/Pure/Tools/build.scala \ + src/Pure/Tools/build_docker.scala \ + src/Pure/Tools/build_job.scala \ + src/Pure/Tools/check_keywords.scala \ + src/Pure/Tools/debugger.scala \ + src/Pure/Tools/doc.scala \ + src/Pure/Tools/dump.scala \ + src/Pure/Tools/fontforge.scala \ + src/Pure/Tools/java_monitor.scala \ + src/Pure/Tools/logo.scala \ + src/Pure/Tools/mkroot.scala \ + src/Pure/Tools/phabricator.scala \ + src/Pure/Tools/print_operation.scala \ + src/Pure/Tools/profiling_report.scala \ + src/Pure/Tools/scala_project.scala \ + src/Pure/Tools/server.scala \ + src/Pure/Tools/server_commands.scala \ + src/Pure/Tools/simplifier_trace.scala \ + src/Pure/Tools/spell_checker.scala \ + src/Pure/Tools/task_statistics.scala \ + src/Pure/Tools/update.scala \ + src/Pure/Tools/update_cartouches.scala \ + src/Pure/Tools/update_comments.scala \ + src/Pure/Tools/update_header.scala \ + src/Pure/Tools/update_then.scala \ + src/Pure/Tools/update_theorems.scala \ + src/Pure/library.scala \ + src/Pure/pure_thy.scala \ + src/Pure/term.scala \ + src/Pure/term_xml.scala \ + src/Pure/thm_name.scala \ + src/Tools/Graphview/graph_file.scala \ + src/Tools/Graphview/graph_panel.scala \ + src/Tools/Graphview/graphview.scala \ + src/Tools/Graphview/layout.scala \ + src/Tools/Graphview/main_panel.scala \ + src/Tools/Graphview/metrics.scala \ + src/Tools/Graphview/model.scala \ + src/Tools/Graphview/mutator.scala \ + src/Tools/Graphview/mutator_dialog.scala \ + src/Tools/Graphview/mutator_event.scala \ + src/Tools/Graphview/popups.scala \ + src/Tools/Graphview/shapes.scala \ + src/Tools/Graphview/tree_panel.scala \ + src/Tools/VSCode/src/build_vscode.scala \ + src/Tools/VSCode/src/channel.scala \ + src/Tools/VSCode/src/dynamic_output.scala \ + src/Tools/VSCode/src/language_server.scala \ + src/Tools/VSCode/src/lsp.scala \ + src/Tools/VSCode/src/preview_panel.scala \ + src/Tools/VSCode/src/state_panel.scala \ + src/Tools/VSCode/src/textmate_grammar.scala \ + src/Tools/VSCode/src/vscode_model.scala \ + src/Tools/VSCode/src/vscode_rendering.scala \ + src/Tools/VSCode/src/vscode_resources.scala \ + src/Tools/VSCode/src/vscode_spell_checker.scala \ + src/Tools/jEdit/src/active.scala \ + src/Tools/jEdit/src/base_plugin.scala \ + src/Tools/jEdit/src/completion_popup.scala \ + src/Tools/jEdit/src/context_menu.scala \ + src/Tools/jEdit/src/debugger_dockable.scala \ + src/Tools/jEdit/src/dockable.scala \ + src/Tools/jEdit/src/document_model.scala \ + src/Tools/jEdit/src/document_view.scala \ + src/Tools/jEdit/src/documentation_dockable.scala \ + src/Tools/jEdit/src/fold_handling.scala \ + src/Tools/jEdit/src/font_info.scala \ + src/Tools/jEdit/src/graphview_dockable.scala \ + src/Tools/jEdit/src/info_dockable.scala \ + src/Tools/jEdit/src/isabelle.scala \ + src/Tools/jEdit/src/isabelle_encoding.scala \ + src/Tools/jEdit/src/isabelle_export.scala \ + src/Tools/jEdit/src/isabelle_options.scala \ + src/Tools/jEdit/src/isabelle_session.scala \ + src/Tools/jEdit/src/isabelle_vfs.scala \ + src/Tools/jEdit/src/jedit_bibtex.scala \ + src/Tools/jEdit/src/jedit_editor.scala \ + src/Tools/jEdit/src/jedit_lib.scala \ + src/Tools/jEdit/src/jedit_options.scala \ + src/Tools/jEdit/src/jedit_rendering.scala \ + src/Tools/jEdit/src/jedit_resources.scala \ + src/Tools/jEdit/src/jedit_sessions.scala \ + src/Tools/jEdit/src/jedit_spell_checker.scala \ + src/Tools/jEdit/src/keymap_merge.scala \ + src/Tools/jEdit/src/main.scala \ + src/Tools/jEdit/src/main_plugin.scala \ + src/Tools/jEdit/src/monitor_dockable.scala \ + src/Tools/jEdit/src/output_dockable.scala \ + src/Tools/jEdit/src/pide_docking_framework.scala \ + src/Tools/jEdit/src/pretty_text_area.scala \ + src/Tools/jEdit/src/pretty_tooltip.scala \ + src/Tools/jEdit/src/process_indicator.scala \ + src/Tools/jEdit/src/protocol_dockable.scala \ + src/Tools/jEdit/src/query_dockable.scala \ + src/Tools/jEdit/src/raw_output_dockable.scala \ + src/Tools/jEdit/src/rich_text_area.scala \ + src/Tools/jEdit/src/session_build.scala \ + src/Tools/jEdit/src/simplifier_trace_dockable.scala \ + src/Tools/jEdit/src/simplifier_trace_window.scala \ + src/Tools/jEdit/src/sledgehammer_dockable.scala \ + src/Tools/jEdit/src/state_dockable.scala \ + src/Tools/jEdit/src/status_widget.scala \ + src/Tools/jEdit/src/symbols_dockable.scala \ + src/Tools/jEdit/src/syntax_style.scala \ + src/Tools/jEdit/src/syslog_dockable.scala \ + src/Tools/jEdit/src/text_overview.scala \ + src/Tools/jEdit/src/text_structure.scala \ + src/Tools/jEdit/src/theories_dockable.scala \ + src/Tools/jEdit/src/timing_dockable.scala \ + src/Tools/jEdit/src/token_markup.scala +services = \ + isabelle.Bibtex$File_Format \ + isabelle.Document_Build$Build_Engine \ + isabelle.Document_Build$LuaLaTeX_Engine \ + isabelle.Document_Build$PDFLaTeX_Engine \ + isabelle.ML_Statistics$Handler \ + isabelle.Print_Operation$Handler \ + isabelle.Scala$Handler \ + isabelle.Scala_Functions \ + isabelle.Server_Commands \ + isabelle.Sessions$File_Format \ + isabelle.Simplifier_Trace$Handler \ + isabelle.Tools \ + isabelle.nitpick.Kodkod$Handler \ + isabelle.nitpick.Scala_Functions \ + isabelle.spark.SPARK$Load_Command1 \ + isabelle.spark.SPARK$Load_Command2 diff --git a/etc/settings b/etc/settings --- a/etc/settings +++ b/etc/settings @@ -1,189 +1,171 @@ # -*- shell-script -*- :mode=shellscript: # # Isabelle system settings. # # Important notes: # * See the "system" manual for explanations on Isabelle settings # * User settings go into $ISABELLE_HOME_USER/etc/settings # * DO NOT EDIT the repository copy of this file! # * DO NOT COPY this file into the $ISABELLE_HOME_USER directory! ### ### Isabelle/Scala ### ISABELLE_JAVA_SYSTEM_OPTIONS="-server -Dfile.encoding=UTF-8 -Disabelle.threads=0" ISABELLE_TOOL_JAVA_OPTIONS="-Djava.awt.headless=true -Xms512m -Xmx4g -Xss16m" ISABELLE_JAVAC_OPTIONS="-encoding UTF-8 -Xlint:-options -deprecation -source 11 -target 11" ISABELLE_SCALAC_OPTIONS="-encoding UTF-8 -Wconf:cat=other-match-analysis:silent -feature -deprecation -target:11 -Xsource:3 -J-Xms512m -J-Xmx4g -J-Xss16m" -classpath "$ISABELLE_HOME/lib/classes/Pure.jar" - -isabelle_scala_service 'isabelle.Tools' -[ -d "$ISABELLE_HOME/Admin" ] && isabelle_scala_service 'isabelle.Admin_Tools' - -isabelle_scala_service 'isabelle.Scala_Functions' - -isabelle_scala_service 'isabelle.Sessions$File_Format' -isabelle_scala_service 'isabelle.Bibtex$File_Format' - -isabelle_scala_service 'isabelle.ML_Statistics$Handler' -isabelle_scala_service 'isabelle.Scala$Handler' -isabelle_scala_service 'isabelle.Print_Operation$Handler' -isabelle_scala_service 'isabelle.Simplifier_Trace$Handler' -isabelle_scala_service 'isabelle.Server_Commands' - -isabelle_scala_service 'isabelle.Document_Build$LuaLaTeX_Engine' -isabelle_scala_service 'isabelle.Document_Build$PDFLaTeX_Engine' -isabelle_scala_service 'isabelle.Document_Build$Build_Engine' +ISABELLE_SCALA_JAR="$ISABELLE_HOME/lib/classes/isabelle.jar" #paranoia settings -- avoid intrusion of alien options unset "_JAVA_OPTIONS" unset "JAVA_TOOL_OPTIONS" #paranoia settings -- avoid problems of Java/Swing versus XIM/IBus etc. unset XMODIFIERS ### ### Interactive sessions (cf. isabelle console) ### ISABELLE_LINE_EDITOR="rlwrap" ### ### Batch sessions (cf. isabelle build) ### ISABELLE_BUILD_OPTIONS="" ### ### Document preparation (cf. isabelle latex) ### if [ "$ISABELLE_PLATFORM_FAMILY" = "windows" ]; then ISABELLE_PDFLATEX="pdflatex -interaction=nonstopmode -c-style-errors" else ISABELLE_PDFLATEX="pdflatex -interaction=nonstopmode -file-line-error" fi ISABELLE_LUALATEX="lualatex --interaction=nonstopmode --file-line-error" ISABELLE_BIBTEX="bibtex" ISABELLE_MAKEINDEX="makeindex -c -q" ISABELLE_EPSTOPDF="epstopdf" ### ### Misc path settings ### isabelle_directory '~' isabelle_directory '$ISABELLE_HOME_USER' isabelle_directory '~~' ISABELLE_COMPONENT_REPOSITORY="https://isabelle.sketis.net/components" ISABELLE_COMPONENTS_BASE="$USER_HOME/.isabelle/contrib" # The place for user configuration, heap files, etc. if [ -z "$ISABELLE_IDENTIFIER" ]; then ISABELLE_HOME_USER="$USER_HOME/.isabelle" else ISABELLE_HOME_USER="$USER_HOME/.isabelle/$ISABELLE_IDENTIFIER" fi # Where to look for isabelle tools (multiple dirs separated by ':'). ISABELLE_TOOLS="$ISABELLE_HOME/lib/Tools" # Location for temporary files (should be on a local file system). ISABELLE_TMP_PREFIX="${TMPDIR:-/tmp}/isabelle-$USER" # Heap locations. ISABELLE_HEAPS="$ISABELLE_HOME_USER/heaps" ISABELLE_HEAPS_SYSTEM="$ISABELLE_HOME/heaps" # HTML browser info. ISABELLE_BROWSER_INFO="$ISABELLE_HOME_USER/browser_info" ISABELLE_BROWSER_INFO_SYSTEM="$ISABELLE_HOME/browser_info" # Site settings check -- just to make it a little bit harder to copy this file verbatim! [ -n "$ISABELLE_SITE_SETTINGS_PRESENT" ] && \ { echo >&2 "### Isabelle site settings already present! Maybe copied etc/settings in full?"; } ISABELLE_SITE_SETTINGS_PRESENT=true ### ### Default logic ### ISABELLE_LOGIC=HOL ### ### Docs and external files ### # Where to look for docs (multiple dirs separated by ':'). ISABELLE_DOCS="$ISABELLE_HOME/doc" ISABELLE_DOCS_RELEASE_NOTES="~~/ANNOUNCE:~~/README:~~/NEWS:~~/COPYRIGHT:~~/CONTRIBUTORS:~~/contrib/README:~~/src/Tools/jEdit/README:~~/README_REPOSITORY" ISABELLE_DOCS_EXAMPLES="~~/src/HOL/Examples/Seq.thy:~~/src/HOL/Examples/Drinker.thy:~~/src/HOL/Examples/ML.thy:~~/src/HOL/Unix/Unix.thy:~~/src/Tools/SML/Examples.thy:~~/src/Pure/ROOT.ML" # "open" within desktop environment (potentially asynchronous) case "$ISABELLE_PLATFORM_FAMILY" in linux) ISABELLE_OPEN="xdg-open" ;; macos) ISABELLE_OPEN="open" ;; windows) ISABELLE_OPEN="cygstart" ;; esac PDF_VIEWER="$ISABELLE_OPEN" ISABELLE_EXTERNAL_FILES="bmp:eps:gif:jpeg:jpg:pdf:png:xmp" ### ### Symbol rendering ### ISABELLE_SYMBOLS="$ISABELLE_HOME/etc/symbols:$ISABELLE_HOME_USER/etc/symbols" ### ### OCaml ### ISABELLE_OPAM_ROOT="$USER_HOME/.opam" ISABELLE_OCAML_VERSION="ocaml-base-compiler.4.12.0" ### ### Haskell ### ISABELLE_STACK_ROOT="$USER_HOME/.stack" ISABELLE_STACK_RESOLVER="lts-17.10" ISABELLE_GHC_VERSION="ghc-8.10.4" ### ### Misc settings ### ISABELLE_GNUPLOT="gnuplot" ISABELLE_FONTFORGE="fontforge" #ISABELLE_MLTON="/usr/bin/mlton" #ISABELLE_SMLNJ="/usr/bin/sml" #ISABELLE_SWIPL="/usr/bin/swipl" diff --git a/lib/Tools/java b/lib/Tools/java --- a/lib/Tools/java +++ b/lib/Tools/java @@ -1,13 +1,15 @@ #!/usr/bin/env bash # # Author: Makarius # # DESCRIPTION: invoke Java within the Isabelle environment eval "declare -a JAVA_ARGS=($ISABELLE_JAVA_SYSTEM_OPTIONS)" +isabelle_setup_classpath + [ -n "$CLASSPATH" ] && classpath "$CLASSPATH" unset CLASSPATH isabelle_java java "${JAVA_ARGS[@]}" \ -classpath "$(platform_path "$ISABELLE_CLASSPATH")" "$@" diff --git a/lib/scripts/getfunctions b/lib/scripts/getfunctions --- a/lib/scripts/getfunctions +++ b/lib/scripts/getfunctions @@ -1,311 +1,318 @@ # -*- shell-script -*- :mode=shellscript: # # Author: Makarius # # Isabelle shell functions, with on-demand re-initialization for # non-interactive bash processess. NB: bash shell functions are not portable # and may be dropped by aggressively POSIX-conformant versions of /bin/sh. unset CDPATH if type splitarray >/dev/null 2>/dev/null then : else if [ "$OSTYPE" = cygwin ]; then function platform_path() { cygpath -i -C UTF8 -w -p "$@"; } function standard_path() { cygpath -i -u -p "$@" | tr -d '\r'; } else function platform_path() { echo "$@"; } function standard_path() { echo "$@"; } fi export -f platform_path standard_path #GNU tar (notably on macOS) function tar() { if [ -f "$ISABELLE_TAR" ]; then "$ISABELLE_TAR" "$@" else "$(type -P tar)" "$@" fi } export -f tar #OCaml management via OPAM function isabelle_opam () { if [ -z "$ISABELLE_OPAM" ]; then echo "Unknown ISABELLE_OPAM -- OCaml management tools unavailable" >&2 return 127 else env OPAMROOT="$ISABELLE_OPAM_ROOT" OPAMCOLOR="never" "$ISABELLE_OPAM" "$@" fi } export -f isabelle_opam #GHC management via Stack function isabelle_stack () { if [ -z "$ISABELLE_STACK" ]; then echo "Unknown ISABELLE_STACK -- GHC management tools unavailable" >&2 return 127 else env STACK_ROOT="$(platform_path "$ISABELLE_STACK_ROOT")" "$ISABELLE_STACK" --resolver "$ISABELLE_STACK_RESOLVER" --compiler "$ISABELLE_GHC_VERSION" "$@" fi } export -f isabelle_stack #robust invocation via ISABELLE_JDK_HOME function isabelle_jdk () { if [ -z "$ISABELLE_JDK_HOME" ]; then echo "Unknown ISABELLE_JDK_HOME -- Java development tools unavailable" >&2 return 127 else local PRG="$1"; shift "$ISABELLE_JDK_HOME/bin/$PRG" "$@" fi } export -f isabelle_jdk #robust invocation via JAVA_HOME function isabelle_java () { if [ -z "$JAVA_HOME" ]; then echo "Unknown JAVA_HOME -- Java unavailable" >&2 return 127 else local PRG="$1"; shift "$JAVA_HOME/bin/$PRG" "$@" fi } export -f isabelle_java #robust invocation via SCALA_HOME function isabelle_scala () { if [ -z "$JAVA_HOME" ]; then echo "Unknown JAVA_HOME -- Java unavailable" >&2 return 127 elif [ -z "$SCALA_HOME" ]; then echo "Unknown SCALA_HOME -- Scala unavailable" >&2 return 127 else local PRG="$1"; shift "$SCALA_HOME/bin/$PRG" "$@" fi } export -f isabelle_scala #classpath function classpath () { local X="" for X in "$@" do if [ -z "$ISABELLE_CLASSPATH" ]; then ISABELLE_CLASSPATH="$X" else ISABELLE_CLASSPATH="$ISABELLE_CLASSPATH:$X" fi done export ISABELLE_CLASSPATH } export -f classpath #java_library function java_library () { local X="" for X in "$@" do case "$ISABELLE_PLATFORM_FAMILY" in linux) if [ -z "$LD_LIBRARY_PATH" ]; then export LD_LIBRARY_PATH="$X" else export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$X" fi ;; macos) if [ -z "$JAVA_LIBRARY_PATH" ]; then export JAVA_LIBRARY_PATH="$X" else export JAVA_LIBRARY_PATH="$JAVA_LIBRARY_PATH:$X" fi ;; windows) if [ -z "$PATH" ]; then export PATH="$X" else export PATH="$PATH:$X" fi ;; esac done export ISABELLE_CLASSPATH } export -f java_library #Isabelle fonts function isabelle_fonts () { local X="" for X in "$@" do if [ -z "$ISABELLE_FONTS" ]; then ISABELLE_FONTS="$X" else ISABELLE_FONTS="$ISABELLE_FONTS:$X" fi done export ISABELLE_FONTS } export -f isabelle_fonts function isabelle_fonts_hidden () { local X="" for X in "$@" do if [ -z "$ISABELLE_FONTS_HIDDEN" ]; then ISABELLE_FONTS_HIDDEN="$X" else ISABELLE_FONTS_HIDDEN="$ISABELLE_FONTS_HIDDEN:$X" fi done export ISABELLE_FONTS_HIDDEN } export -f isabelle_fonts_hidden #Isabelle/Scala services function isabelle_scala_service () { local X="" for X in "$@" do if [ -z "$ISABELLE_SCALA_SERVICES" ]; then ISABELLE_SCALA_SERVICES="$X" else ISABELLE_SCALA_SERVICES="$ISABELLE_SCALA_SERVICES:$X" fi done export ISABELLE_SCALA_SERVICES } export -f isabelle_scala_service #Special directories function isabelle_directory () { local X="" for X in "$@" do if [ -z "$ISABELLE_DIRECTORIES" ]; then ISABELLE_DIRECTORIES="$X" else ISABELLE_DIRECTORIES="$ISABELLE_DIRECTORIES:$X" fi done export ISABELLE_DIRECTORIES } export -f isabelle_directory +#setup classpath +function isabelle_setup_classpath +{ + classpath "$(isabelle_java java -classpath "$(platform_path "$ISABELLE_SETUP_JAR")" isabelle.setup.Setup classpath)" +} +export -f isabelle_setup_classpath + #administrative build -if [ -e "$ISABELLE_HOME/Admin/build" ]; then - function isabelle_admin_build () +function isabelle_admin_build () +{ { - "$ISABELLE_HOME/Admin/build" "$@" - } -else - function isabelle_admin_build () { return 0; } -fi + if [ -e "$ISABELLE_HOME/Admin/build" ]; then + "$ISABELLE_HOME/Admin/build" "$@" + fi + } && isabelle_setup_classpath +} export -f isabelle_admin_build #arrays function splitarray () { SPLITARRAY=() local IFS="$1"; shift local X="" for X in $* do SPLITARRAY["${#SPLITARRAY[@]}"]="$X" done } export -f splitarray #init component tree function init_component () { local COMPONENT="$1" case "$COMPONENT" in /*) ;; *) echo >&2 "Absolute component path required: \"$COMPONENT\"" exit 2 ;; esac if [ -d "$COMPONENT" ]; then if [ -z "$ISABELLE_COMPONENTS" ]; then ISABELLE_COMPONENTS="$COMPONENT" else ISABELLE_COMPONENTS="$ISABELLE_COMPONENTS:$COMPONENT" fi else echo >&2 "### Missing Isabelle component: \"$COMPONENT\"" if [ -z "$ISABELLE_COMPONENTS_MISSING" ]; then ISABELLE_COMPONENTS_MISSING="$COMPONENT" else ISABELLE_COMPONENTS_MISSING="$ISABELLE_COMPONENTS_MISSING:$COMPONENT" fi fi if [ -f "$COMPONENT/etc/settings" ]; then source "$COMPONENT/etc/settings" local RC="$?" if [ "$RC" -ne 0 ]; then echo >&2 "Return code $RC from bash script: \"$COMPONENT/etc/settings\"" exit 2 fi fi if [ -f "$COMPONENT/etc/components" ]; then init_components "$COMPONENT" "$COMPONENT/etc/components" fi } export -f init_component #init component forest function init_components () { local REPLY="" local BASE="$1" local CATALOG="$2" local COMPONENT="" local -a COMPONENTS=() if [ ! -f "$CATALOG" ]; then echo >&2 "Bad component catalog file: \"$CATALOG\"" exit 2 fi { while { unset REPLY; read -r; test "$?" = 0 -o -n "$REPLY"; } do case "$REPLY" in \#* | "") ;; /*) COMPONENTS["${#COMPONENTS[@]}"]="$REPLY" ;; *) COMPONENTS["${#COMPONENTS[@]}"]="$BASE/$REPLY" ;; esac done } < "$CATALOG" for COMPONENT in "${COMPONENTS[@]}" do init_component "$COMPONENT" done } export -f init_components fi diff --git a/lib/services/java.nio.charset.spi.CharsetProvider b/lib/services/java.nio.charset.spi.CharsetProvider new file mode 100644 --- /dev/null +++ b/lib/services/java.nio.charset.spi.CharsetProvider @@ -0,0 +1,1 @@ +isabelle.Isabelle_Charset_Provider diff --git a/src/Doc/JEdit/JEdit.thy b/src/Doc/JEdit/JEdit.thy --- a/src/Doc/JEdit/JEdit.thy +++ b/src/Doc/JEdit/JEdit.thy @@ -1,2245 +1,2244 @@ (*:maxLineLen=78:*) theory JEdit imports Base begin chapter \Introduction\ section \Concepts and terminology\ text \ Isabelle/jEdit is a Prover IDE that integrates \<^emph>\parallel proof checking\ @{cite "Wenzel:2009" and "Wenzel:2013:ITP"} with \<^emph>\asynchronous user interaction\ @{cite "Wenzel:2010" and "Wenzel:2012:UITP-EPTCS" and "Wenzel:2014:ITP-PIDE" and "Wenzel:2014:UITP"}, based on a document-oriented approach to \<^emph>\continuous proof processing\ @{cite "Wenzel:2011:CICM" and "Wenzel:2012" and "Wenzel:2018:FIDE" and "Wenzel:2019:MKM"}. Many concepts and system components are fit together in order to make this work. The main building blocks are as follows. \<^descr>[Isabelle/ML] is the implementation and extension language of Isabelle, see also @{cite "isabelle-implementation"}. It is integrated into the logical context of Isabelle/Isar and allows to manipulate logical entities directly. Arbitrary add-on tools may be implemented for object-logics such as Isabelle/HOL. \<^descr>[Isabelle/Scala] is the system programming language of Isabelle. It extends the pure logical environment of Isabelle/ML towards the outer world of graphical user interfaces, text editors, IDE frameworks, web services, SSH servers, SQL databases etc. Special infrastructure allows to transfer algebraic datatypes and formatted text easily between ML and Scala, using asynchronous protocol commands. \<^descr>[PIDE] is a general framework for Prover IDEs based on Isabelle/Scala. It is built around a concept of parallel and asynchronous document processing, which is supported natively by the parallel proof engine that is implemented in Isabelle/ML. The traditional prover command loop is given up; instead there is direct support for editing of source text, with rich formal markup for GUI rendering. \<^descr>[jEdit] is a sophisticated text editor\<^footnote>\\<^url>\http://www.jedit.org\\ implemented in Java\<^footnote>\\<^url>\https://adoptopenjdk.net\\. It is easily extensible by plugins written in any language that works on the JVM. In the context of Isabelle this is always Scala\<^footnote>\\<^url>\https://www.scala-lang.org\\. \<^descr>[Isabelle/jEdit] is the main application of the PIDE framework and the default user-interface for Isabelle. It targets both beginners and experts. Technically, Isabelle/jEdit consists of the original jEdit code base with minimal patches and a special plugin for Isabelle. This is integrated as a desktop application for the main operating system families: Linux, Windows, macOS. End-users of Isabelle download and run a standalone application that exposes jEdit as a text editor on the surface. Thus there is occasionally a tendency to apply the name ``jEdit'' to any of the Isabelle Prover IDE aspects, without proper differentiation. When discussing these PIDE building blocks in public forums, mailing lists, or even scientific publications, it is particularly important to distinguish Isabelle/ML versus Standard ML, Isabelle/Scala versus Scala, Isabelle/jEdit versus jEdit. \ section \The Isabelle/jEdit Prover IDE\ text \ \begin{figure}[!htb] \begin{center} \includegraphics[width=\textwidth]{isabelle-jedit} \end{center} \caption{The Isabelle/jEdit Prover IDE} \label{fig:isabelle-jedit} \end{figure} Isabelle/jEdit (\figref{fig:isabelle-jedit}) consists of some plugins for the jEdit text editor, while preserving its overall look-and-feel. The main plugin is called ``Isabelle'' and has its own menu \<^emph>\Plugins~/ Isabelle\ with access to several actions and add-on panels (see also \secref{sec:dockables}), as well as \<^emph>\Plugins~/ Plugin Options~/ Isabelle\ (see also \secref{sec:options}). The options allow to specify a logic session name, but the same selector is also accessible in the \<^emph>\Theories\ panel (\secref{sec:theories}). After startup of the Isabelle plugin, the selected logic session image is provided automatically by the Isabelle build tool @{cite "isabelle-system"}: if it is absent or outdated wrt.\ its sources, the build process updates it within the running text editor. Prover IDE functionality is fully activated after successful termination of the build process. A failure may require changing some options and restart of the Isabelle plugin or application. Changing the logic session requires a restart of the whole application to take effect. \<^medskip> The main job of the Prover IDE is to manage sources and their changes, taking the logical structure as a formal document into account (see also \secref{sec:document-model}). The editor and the prover are connected asynchronously without locking. The prover is free to organize the checking of the formal text in parallel on multiple cores, and provides feedback via markup, which is rendered in the editor via colors, boxes, squiggly underlines, hyperlinks, popup windows, icons, clickable output etc. Using the mouse together with the modifier key \<^verbatim>\CONTROL\ (Linux, Windows) or \<^verbatim>\COMMAND\ (macOS) exposes formal content via tooltips and/or hyperlinks (see also \secref{sec:tooltips-hyperlinks}). Output (in popups etc.) may be explored recursively, using the same techniques as in the editor source buffer. Thus the Prover IDE gives an impression of direct access to formal content of the prover within the editor, but in reality only certain aspects are exposed, according to the possibilities of the prover and its add-on tools. \ subsection \Documentation\ text \ The \<^emph>\Documentation\ panel of Isabelle/jEdit provides access to some example theory files and the standard Isabelle documentation. PDF files are opened by regular desktop operations of the underlying platform. The section ``Original jEdit Documentation'' contains the original \<^emph>\User's Guide\ of this sophisticated text editor. The same is accessible via the \<^verbatim>\Help\ menu or \<^verbatim>\F1\ keyboard shortcut, using the built-in HTML viewer of Java/Swing. The latter also includes \<^emph>\Frequently Asked Questions\ and documentation of individual plugins. Most of the information about jEdit is relevant for Isabelle/jEdit as well, but users need to keep in mind that defaults sometimes differ, and the official jEdit documentation does not know about the Isabelle plugin with its support for continuous checking of formal source text: jEdit is a plain text editor, but Isabelle/jEdit is a Prover IDE. \ subsection \Plugins\ text \ The \<^emph>\Plugin Manager\ of jEdit allows to augment editor functionality by JVM modules (jars) that are provided by the central plugin repository, which is accessible via various mirror sites. Connecting to the plugin server-infrastructure of the jEdit project allows to update bundled plugins or to add further functionality. This needs to be done with the usual care for such an open bazaar of contributions. Arbitrary combinations of add-on features are apt to cause problems. It is advisable to start with the default configuration of Isabelle/jEdit and develop a sense how it is meant to work, before loading too many other plugins. \<^medskip> The \<^emph>\Isabelle\ plugin is responsible for the main Prover IDE functionality of Isabelle/jEdit: it manages the prover session in the background. A few additional plugins are bundled with Isabelle/jEdit for convenience or out of necessity, notably \<^emph>\Console\ with its \<^emph>\Scala\ sub-plugin (\secref{sec:scala-console}) and \<^emph>\SideKick\ with some Isabelle-specific parsers for document tree structure (\secref{sec:sidekick}). The \<^emph>\Navigator\ plugin is particularly important for hyperlinks within the formal document-model (\secref{sec:tooltips-hyperlinks}). Further plugins (e.g.\ \<^emph>\ErrorList\, \<^emph>\Code2HTML\) are included to saturate the dependencies of bundled plugins, but have no particular use in Isabelle/jEdit. \ subsection \Options \label{sec:options}\ text \ Both jEdit and Isabelle have distinctive management of persistent options. Regular jEdit options are accessible via the dialogs \<^emph>\Utilities~/ Global Options\ or \<^emph>\Plugins~/ Plugin Options\, with a second chance to flip the two within the central options dialog. Changes are stored in \<^path>\$JEDIT_SETTINGS/properties\ and \<^path>\$JEDIT_SETTINGS/keymaps\. Isabelle system options are managed by Isabelle/Scala and changes are stored in \<^path>\$ISABELLE_HOME_USER/etc/preferences\, independently of other jEdit properties. See also @{cite "isabelle-system"}, especially the coverage of sessions and command-line tools like @{tool build} or @{tool options}. Those Isabelle options that are declared as \<^verbatim>\public\ are configurable in Isabelle/jEdit via \<^emph>\Plugin Options~/ Isabelle~/ General\. Moreover, there are various options for rendering document content, which are configurable via \<^emph>\Plugin Options~/ Isabelle~/ Rendering\. Thus \<^emph>\Plugin Options~/ Isabelle\ in jEdit provides a view on a subset of Isabelle system options. Note that some of these options affect general parameters that are relevant outside Isabelle/jEdit as well, e.g.\ @{system_option threads} or @{system_option parallel_proofs} for the Isabelle build tool @{cite "isabelle-system"}, but it is possible to use the settings variable @{setting ISABELLE_BUILD_OPTIONS} to change defaults for batch builds without affecting the Prover IDE. The jEdit action @{action_def isabelle.options} opens the options dialog for the Isabelle plugin; it can be mapped to editor GUI elements as usual. \<^medskip> Options are usually loaded on startup and saved on shutdown of Isabelle/jEdit. Editing the generated \<^path>\$JEDIT_SETTINGS/properties\ or \<^path>\$ISABELLE_HOME_USER/etc/preferences\ manually while the application is running may cause lost updates! \ subsection \Keymaps\ text \ Keyboard shortcuts are managed as a separate concept of \<^emph>\keymap\ that is configurable via \<^emph>\Global Options~/ Shortcuts\. The \<^verbatim>\imported\ keymap is derived from the initial environment of properties that is available at the first start of the editor; afterwards the keymap file takes precedence and is no longer affected by change of default properties. Users may modify their keymap later, but this can lead to conflicts with - \<^verbatim>\shortcut\ properties in \<^file>\$JEDIT_HOME/dist/properties/jEdit.props\. + \<^verbatim>\shortcut\ properties in \<^file>\$JEDIT_HOME/properties/jEdit.props\. The action @{action_def "isabelle.keymap-merge"} helps to resolve pending Isabelle keymap changes wrt. the current jEdit keymap; non-conflicting changes are applied implicitly. This action is automatically invoked on Isabelle/jEdit startup. \ section \Command-line invocation \label{sec:command-line}\ text \ Isabelle/jEdit is normally invoked as a single-instance desktop application, based on platform-specific executables for Linux, Windows, macOS. It is also possible to invoke the Prover IDE on the command-line, with some extra options and environment settings. The command-line usage of @{tool_def jedit} is as follows: @{verbatim [display] \Usage: isabelle jedit [OPTIONS] [FILES ...] Options are: -A NAME ancestor session for option -R (default: parent) -D NAME=X set JVM system property -J OPTION add JVM runtime option (default $JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS) -R NAME build image with requirements from other sessions -b build only -d DIR include session directory -f fresh build -i NAME include session in name-space of theories -j OPTION add jEdit runtime option (default $JEDIT_OPTIONS) -l NAME logic image name -m MODE add print mode for output -n no build of session image on startup -p CMD ML process command prefix (process policy) -s system build mode for session image (system_heaps=true) -u user build mode for session image (system_heaps=false) Start jEdit with Isabelle plugin setup and open FILES (default "$USER_HOME/Scratch.thy" or ":" for empty buffer).\} The \<^verbatim>\-l\ option specifies the session name of the logic image to be used for proof processing. Additional session root directories may be included via option \<^verbatim>\-d\ to augment the session name space (see also @{cite "isabelle-system"}). By default, the specified image is checked and built on demand, but option \<^verbatim>\-n\ bypasses the implicit build process for the selected session image. Options \<^verbatim>\-s\ and \<^verbatim>\-u\ override the default system option @{system_option system_heaps}: this determines where to store the session image of @{tool build}. The \<^verbatim>\-R\ option builds an auxiliary logic image with all theories from other sessions that are not already present in its parent; it also opens the session \<^verbatim>\ROOT\ entry in the editor to facilitate editing of the main session. The \<^verbatim>\-A\ option specifies and alternative ancestor session for option \<^verbatim>\-R\: this allows to restructure the hierarchy of session images on the spot. The \<^verbatim>\-i\ option includes additional sessions into the name-space of theories: multiple occurrences are possible. The \<^verbatim>\-m\ option specifies additional print modes for the prover process. Note that the system option @{system_option_ref jedit_print_mode} allows to do the same persistently (e.g.\ via the \<^emph>\Plugin Options\ dialog of Isabelle/jEdit), without requiring command-line invocation. The \<^verbatim>\-J\ and \<^verbatim>\-j\ options pass additional low-level options to the JVM or jEdit, respectively. The defaults are provided by the Isabelle settings environment @{cite "isabelle-system"}, but note that these only work for the command-line tool described here, and not the desktop application. The \<^verbatim>\-D\ option allows to define JVM system properties; this is passed directly to the underlying \<^verbatim>\java\ process. The \<^verbatim>\-b\ and \<^verbatim>\-f\ options control the self-build mechanism of - Isabelle/jEdit. This is only relevant for building from sources, which also - requires an auxiliary \<^verbatim>\jedit_build\ component from - \<^url>\https://isabelle.in.tum.de/components\. The official Isabelle release - already includes a pre-built version of Isabelle/jEdit. + Isabelle/Scala/PIDE/jEdit. This is only relevant for building from sources, + the official Isabelle release already includes a pre-built version of + Isabelle/jEdit. \<^bigskip> It is also possible to connect to an already running Isabelle/jEdit process via @{tool_def jedit_client}: @{verbatim [display] \Usage: isabelle jedit_client [OPTIONS] [FILES ...] Options are: -c only check presence of server -n only report server name -s NAME server name (default "Isabelle") Connect to already running Isabelle/jEdit instance and open FILES\} The \<^verbatim>\-c\ option merely checks the presence of the server, producing a process return-code. The \<^verbatim>\-n\ option reports the server name, and the \<^verbatim>\-s\ option provides a different server name. The default server name is the official distribution name (e.g.\ \<^verbatim>\Isabelle2021\). Thus @{tool jedit_client} can connect to the Isabelle desktop application without further options. The \<^verbatim>\-p\ option allows to override the implicit default of the system option @{system_option_ref ML_process_policy} for ML processes started by the Prover IDE, e.g. to control CPU affinity on multiprocessor systems. The JVM system property \<^verbatim>\isabelle.jedit_server\ provides a different server name, e.g.\ use \<^verbatim>\isabelle jedit -Disabelle.jedit_server=\\name\ and \<^verbatim>\isabelle jedit_client -s\~\name\ to connect later on. \ section \GUI rendering\ text \ Isabelle/jEdit is a classic Java/AWT/Swing application: its GUI rendering usually works well, but there are technical side-conditions on the Java window system and graphics engine. When researching problems and solutions on the Web, it often helps to include other well-known Swing applications, notably IntelliJ IDEA and Netbeans. \ subsection \Portable and scalable look-and-feel\ text \ In the past, \<^emph>\system look-and-feels\ tried hard to imitate native GUI elements on specific platforms (Windows, macOS/Aqua, Linux/GTK+), but many technical problems have accumulated in recent years (e.g.\ see \secref{sec:problems}). In 2021, we are de-facto back to \<^emph>\portable look-and-feels\, which also happen to be \emph{scalable} on high-resolution displays: \<^item> \<^verbatim>\FlatLaf Light\ is the default for Isabelle/jEdit on all platforms. It generally looks good and adapts itself pretty well to high-resolution displays. \<^item> \<^verbatim>\FlatLaf Dark\ is an alternative, but it requires further changes of editor colors by the user (or by the jEdit plugin \<^verbatim>\Editor Scheme\). Also note that Isabelle/PIDE has its own extensive set of rendering options that need to be revisited. \<^item> \<^verbatim>\Metal\ still works smoothly, although it is stylistically outdated. It can accommodate high-resolution displays via font properties (see below). Changing the look-and-feel in \<^emph>\Global Options~/ Appearance\ often updates the GUI only partially: a full restart of Isabelle/jEdit is required to see the true effect. \ subsection \Adjusting fonts\ text \ The preferred font family for Isabelle/jEdit is \<^verbatim>\Isabelle DejaVu\: it is used by default for the main text area and various GUI elements. The default font sizes attempt to deliver a usable application for common display types, such as ``Full HD'' at $1920 \times 1080$ and ``Ultra HD'' at $3840 \times 2160$. \<^medskip> Isabelle/jEdit provides various options to adjust font sizes in particular GUI elements. Here is a summary of all relevant font properties: \<^item> \<^emph>\Global Options / Text Area / Text font\: the main text area font, which is also used as reference point for various derived font sizes, e.g.\ the \<^emph>\Output\ (\secref{sec:output}) and \<^emph>\State\ (\secref{sec:state-output}) panels. \<^item> \<^emph>\Global Options / Gutter / Gutter font\: the font for the gutter area left of the main text area, e.g.\ relevant for display of line numbers (disabled by default). \<^item> \<^emph>\Global Options / Appearance / Button, menu and label font\ as well as \<^emph>\List and text field font\: this specifies the primary and secondary font for the \<^emph>\Metal\ look-and-feel. \<^item> \<^emph>\Plugin Options / Isabelle / General / Reset Font Size\: the main text area font size for action @{action_ref "isabelle.reset-font-size"}, e.g.\ relevant for quick scaling like in common web browsers. \<^item> \<^emph>\Plugin Options / Console / General / Font\: the console window font, e.g.\ relevant for Isabelle/Scala command-line. \ chapter \Augmented jEdit functionality\ section \Dockable windows \label{sec:dockables}\ text \ In jEdit terminology, a \<^emph>\view\ is an editor window with one or more \<^emph>\text areas\ that show the content of one or more \<^emph>\buffers\. A regular view may be surrounded by \<^emph>\dockable windows\ that show additional information in arbitrary format, not just text; a \<^emph>\plain view\ does not allow dockables. The \<^emph>\dockable window manager\ of jEdit organizes these dockable windows, either as \<^emph>\floating\ windows, or \<^emph>\docked\ panels within one of the four margins of the view. There may be any number of floating instances of some dockable window, but at most one docked instance; jEdit actions that address \<^emph>\the\ dockable window of a particular kind refer to the unique docked instance. Dockables are used routinely in jEdit for important functionality like \<^emph>\HyperSearch Results\ or the \<^emph>\File System Browser\. Plugins often provide a central dockable to access their main functionality, which may be opened by the user on demand. The Isabelle/jEdit plugin takes this approach to the extreme: its plugin menu provides the entry-points to many panels that are managed as dockable windows. Some important panels are docked by default, e.g.\ \<^emph>\Documentation\, \<^emph>\State\, \<^emph>\Theories\ \<^emph>\Output\, \<^emph>\Query\. The user can change this arrangement easily and persistently. Compared to plain jEdit, dockable window management in Isabelle/jEdit is slightly augmented according to the the following principles: \<^item> Floating windows are dependent on the main window as \<^emph>\dialog\ in the sense of Java/AWT/Swing. Dialog windows always stay on top of the view, which is particularly important in full-screen mode. The desktop environment of the underlying platform may impose further policies on such dependent dialogs, in contrast to fully independent windows, e.g.\ some window management functions may be missing. \<^item> Keyboard focus of the main view vs.\ a dockable window is carefully managed according to the intended semantics, as a panel mainly for output or input. For example, activating the \<^emph>\Output\ (\secref{sec:output}) or State (\secref{sec:state-output}) panel via the dockable window manager returns keyboard focus to the main text area, but for \<^emph>\Query\ (\secref{sec:query}) or \<^emph>\Sledgehammer\ \secref{sec:sledgehammer} the focus is given to the main input field of that panel. \<^item> Panels that provide their own text area for output have an additional dockable menu item \<^emph>\Detach\. This produces an independent copy of the current output as a floating \<^emph>\Info\ window, which displays that content independently of ongoing changes of the PIDE document-model. Note that Isabelle/jEdit popup windows (\secref{sec:tooltips-hyperlinks}) provide a similar \<^emph>\Detach\ operation as an icon. \ section \Isabelle symbols \label{sec:symbols}\ text \ Isabelle sources consist of \<^emph>\symbols\ that extend plain ASCII to allow infinitely many mathematical symbols within the formal sources. This works without depending on particular encodings and varying Unicode standards.\<^footnote>\Raw Unicode characters within formal sources compromise portability and reliability in the face of changing interpretation of special features of Unicode, such as Combining Characters or Bi-directional Text.\ See @{cite "Wenzel:2011:CICM"}. For the prover back-end, formal text consists of ASCII characters that are grouped according to some simple rules, e.g.\ as plain ``\<^verbatim>\a\'' or symbolic ``\<^verbatim>\\\''. For the editor front-end, a certain subset of symbols is rendered physically via Unicode glyphs, to show ``\<^verbatim>\\\'' as ``\\\'', for example. This symbol interpretation is specified by the Isabelle system distribution in \<^file>\$ISABELLE_HOME/etc/symbols\ and may be augmented by the user in \<^path>\$ISABELLE_HOME_USER/etc/symbols\. The appendix of @{cite "isabelle-isar-ref"} gives an overview of the standard interpretation of finitely many symbols from the infinite collection. Uninterpreted symbols are displayed literally, e.g.\ ``\<^verbatim>\\\''. Overlap of Unicode characters used in symbol interpretation with informal ones (which might appear e.g.\ in comments) needs to be avoided. Raw Unicode characters within prover source files should be restricted to informal parts, e.g.\ to write text in non-latin alphabets in comments. \ paragraph \Encoding.\ text \Technically, the Unicode interpretation of Isabelle symbols is an \<^emph>\encoding\ called \<^verbatim>\UTF-8-Isabelle\ in jEdit (\<^emph>\not\ in the underlying JVM). It is provided by the Isabelle Base plugin and enabled by default for all source files in Isabelle/jEdit. Sometimes such defaults are reset accidentally, or malformed UTF-8 sequences in the text force jEdit to fall back on a different encoding like \<^verbatim>\ISO-8859-15\. In that case, verbatim ``\<^verbatim>\\\'' will be shown in the text buffer instead of its Unicode rendering ``\\\''. The jEdit menu operation \<^emph>\File~/ Reload with Encoding~/ UTF-8-Isabelle\ helps to resolve such problems (after repairing malformed parts of the text). If the loaded text already contains Unicode sequences that are in conflict with the Isabelle symbol encoding, the fallback-encoding UTF-8 is used and Isabelle symbols remain in literal \<^verbatim>\\\ form. The jEdit menu operation \<^emph>\Utilities~/ Buffer Options~/ Character encoding\ allows to enforce \<^verbatim>\UTF-8-Isabelle\, but this will also change original Unicode text into Isabelle symbols when saving the file! \ paragraph \Font.\ text \Correct rendering via Unicode requires a font that contains glyphs for the corresponding codepoints. There are also various unusual symbols with particular purpose in Isabelle, e.g.\ control symbols and very long arrows. Isabelle/jEdit prefers its own font collection \<^verbatim>\Isabelle DejaVu\, with families \<^verbatim>\Serif\ (default for help texts), \<^verbatim>\Sans\ (default for GUI elements), \<^verbatim>\Mono Sans\ (default for text area). This ensures that all standard Isabelle symbols are shown on the screen (or printer) as expected. Note that a Java/AWT/Swing application can load additional fonts only if they are not installed on the operating system already! Outdated versions of Isabelle fonts that happen to be provided by the operating system prevent Isabelle/jEdit to use its bundled version. This could lead to missing glyphs (black rectangles), when the system version of a font is older than the application version. This problem can be avoided by refraining to ``install'' a copy of the Isabelle fonts in the first place, although it might be tempting to use the same font in other applications. HTML pages generated by Isabelle refer to the same Isabelle fonts as a server-side resource. Thus a web-browser can use that without requiring a locally installed copy. \ paragraph \Input methods.\ text \In principle, Isabelle/jEdit could delegate the problem to produce Isabelle symbols in their Unicode rendering to the underlying operating system and its \<^emph>\input methods\. Regular jEdit also provides various ways to work with \<^emph>\abbreviations\ to produce certain non-ASCII characters. Since none of these standard input methods work satisfactorily for the mathematical characters required for Isabelle, various specific Isabelle/jEdit mechanisms are provided. This is a summary for practically relevant input methods for Isabelle symbols. \<^enum> The \<^emph>\Symbols\ panel: some GUI buttons allow to insert certain symbols in the text buffer. There are also tooltips to reveal the official Isabelle representation with some additional information about \<^emph>\symbol abbreviations\ (see below). \<^enum> Copy/paste from decoded source files: text that is already rendered as Unicode can be re-used for other text. This also works between different applications, e.g.\ Isabelle/jEdit and some web browser or mail client, as long as the same Unicode interpretation of Isabelle symbols is used. \<^enum> Copy/paste from prover output within Isabelle/jEdit. The same principles as for text buffers apply, but note that \<^emph>\copy\ in secondary Isabelle/jEdit windows works via the keyboard shortcuts \<^verbatim>\C+c\ or \<^verbatim>\C+INSERT\, while jEdit menu actions always refer to the primary text area! \<^enum> Completion provided by the Isabelle plugin (see \secref{sec:completion}). Isabelle symbols have a canonical name and optional abbreviations. This can be used with the text completion mechanism of Isabelle/jEdit, to replace a prefix of the actual symbol like \<^verbatim>\\\, or its name preceded by backslash \<^verbatim>\\lambda\, or its ASCII abbreviation \<^verbatim>\%\ by the Unicode rendering. The following table is an extract of the information provided by the standard \<^file>\$ISABELLE_HOME/etc/symbols\ file: \<^medskip> \begin{tabular}{lll} \<^bold>\symbol\ & \<^bold>\name with backslash\ & \<^bold>\abbreviation\ \\\hline \\\ & \<^verbatim>\\lambda\ & \<^verbatim>\%\ \\ \\\ & \<^verbatim>\\Rightarrow\ & \<^verbatim>\=>\ \\ \\\ & \<^verbatim>\\Longrightarrow\ & \<^verbatim>\==>\ \\[0.5ex] \\\ & \<^verbatim>\\And\ & \<^verbatim>\!!\ \\ \\\ & \<^verbatim>\\equiv\ & \<^verbatim>\==\ \\[0.5ex] \\\ & \<^verbatim>\\forall\ & \<^verbatim>\!\ \\ \\\ & \<^verbatim>\\exists\ & \<^verbatim>\?\ \\ \\\ & \<^verbatim>\\longrightarrow\ & \<^verbatim>\-->\ \\ \\\ & \<^verbatim>\\and\ & \<^verbatim>\&\ \\ \\\ & \<^verbatim>\\or\ & \<^verbatim>\|\ \\ \\\ & \<^verbatim>\\not\ & \<^verbatim>\~\ \\ \\\ & \<^verbatim>\\noteq\ & \<^verbatim>\~=\ \\ \\\ & \<^verbatim>\\in\ & \<^verbatim>\:\ \\ \\\ & \<^verbatim>\\notin\ & \<^verbatim>\~:\ \\ \end{tabular} \<^medskip> Note that the above abbreviations refer to the input method. The logical notation provides ASCII alternatives that often coincide, but sometimes deviate. This occasionally causes user confusion with old-fashioned Isabelle source that use ASCII replacement notation like \<^verbatim>\!\ or \<^verbatim>\ALL\ directly in the text. On the other hand, coincidence of symbol abbreviations with ASCII replacement syntax syntax helps to update old theory sources via explicit completion (see also \<^verbatim>\C+b\ explained in \secref{sec:completion}). \ paragraph \Control symbols.\ text \There are some special control symbols to modify the display style of a single symbol (without nesting). Control symbols may be applied to a region of selected text, either using the \<^emph>\Symbols\ panel or keyboard shortcuts or jEdit actions. These editor operations produce a separate control symbol for each symbol in the text, in order to make the whole text appear in a certain style. \<^medskip> \begin{tabular}{llll} \<^bold>\style\ & \<^bold>\symbol\ & \<^bold>\shortcut\ & \<^bold>\action\ \\\hline superscript & \<^verbatim>\\<^sup>\ & \<^verbatim>\C+e UP\ & @{action_ref "isabelle.control-sup"} \\ subscript & \<^verbatim>\\<^sub>\ & \<^verbatim>\C+e DOWN\ & @{action_ref "isabelle.control-sub"} \\ bold face & \<^verbatim>\\<^bold>\ & \<^verbatim>\C+e RIGHT\ & @{action_ref "isabelle.control-bold"} \\ emphasized & \<^verbatim>\\<^emph>\ & \<^verbatim>\C+e LEFT\ & @{action_ref "isabelle.control-emph"} \\ reset & & \<^verbatim>\C+e BACK_SPACE\ & @{action_ref "isabelle.control-reset"} \\ \end{tabular} \<^medskip> To produce a single control symbol, it is also possible to complete on \<^verbatim>\\sup\, \<^verbatim>\\sub\, \<^verbatim>\\bold\, \<^verbatim>\\emph\ as for regular symbols. The emphasized style only takes effect in document output (when used with a cartouche), but not in the editor. \ section \Scala console \label{sec:scala-console}\ text \ The \<^emph>\Console\ plugin manages various shells (command interpreters), e.g.\ \<^emph>\BeanShell\, which is the official jEdit scripting language, and the cross-platform \<^emph>\System\ shell. Thus the console provides similar functionality than the Emacs buffers \<^verbatim>\*scratch*\ and \<^verbatim>\*shell*\. Isabelle/jEdit extends the repertoire of the console by \<^emph>\Scala\, which is the regular Scala toplevel loop running inside the same JVM process as Isabelle/jEdit itself. This means the Scala command interpreter has access to the JVM name space and state of the running Prover IDE application. The default environment imports the full content of packages \<^verbatim>\isabelle\ and \<^verbatim>\isabelle.jedit\. For example, \<^verbatim>\PIDE\ refers to the Isabelle/jEdit plugin object, and \<^verbatim>\view\ to the current editor view of jEdit. The Scala expression \<^verbatim>\PIDE.snapshot(view)\ makes a PIDE document snapshot of the current buffer within the current editor view: it allows to retrieve document markup in a timeless~/ stateless manner, while the prover continues its processing. This helps to explore Isabelle/Scala functionality interactively. Some care is required to avoid interference with the internals of the running application. \ section \Physical and logical files \label{sec:files}\ text \ File specifications in jEdit follow various formats and conventions according to \<^emph>\Virtual File Systems\, which may be also provided by plugins. This allows to access remote files via the \<^verbatim>\https:\ protocol prefix, for example. Isabelle/jEdit attempts to work with the file-system model of jEdit as far as possible. In particular, theory sources are passed from the editor to the prover, without indirection via the file-system. Thus files don't need to be saved: the editor buffer content is used directly. \ subsection \Local files and environment variables \label{sec:local-files}\ text \ Local files (without URL notation) are particularly important. The file path notation is that of the Java Virtual Machine on the underlying platform. On Windows the preferred form uses backslashes, but happens to accept forward slashes like Unix/POSIX as well. Further differences arise due to Windows drive letters and network shares: thus relative paths (with forward slashes) are portable, but absolute paths are not. File paths in Java are distinct from Isabelle; the latter uses POSIX notation with forward slashes on \<^emph>\all\ platforms. Isabelle/ML on Windows uses Unix-style path notation, with drive letters according to Cygwin (e.g.\ \<^verbatim>\/cygdrive/c\). Environment variables from the Isabelle process may be used freely, e.g.\ \<^file>\$ISABELLE_HOME/etc/symbols\ or \<^file>\$POLYML_HOME/README\. There are special shortcuts: \<^dir>\~\ for \<^dir>\$USER_HOME\ and \<^dir>\~~\ for \<^dir>\$ISABELLE_HOME\. \<^medskip> Since jEdit happens to support environment variables within file specifications as well, it is natural to use similar notation within the editor, e.g.\ in the file-browser. This does not work in full generality, though, due to the bias of jEdit towards platform-specific notation and of Isabelle towards POSIX. Moreover, the Isabelle settings environment is not accessible when starting Isabelle/jEdit via the desktop application wrapper, in contrast to @{tool jedit} run from the command line (\secref{sec:command-line}). Isabelle/jEdit imitates important system settings within the Java process environment, in order to allow easy access to these important places from the editor: \<^verbatim>\$ISABELLE_HOME\, \<^verbatim>\$ISABELLE_HOME_USER\, \<^verbatim>\$JEDIT_HOME\, \<^verbatim>\$JEDIT_SETTINGS\. The file browser of jEdit also includes \<^emph>\Favorites\ for these locations. \<^medskip> Path specifications in prover input or output usually include formal markup that turns it into a hyperlink (see also \secref{sec:tooltips-hyperlinks}). This allows to open the corresponding file in the text editor, independently of the path notation. If the path refers to a directory, it is opened in the jEdit file browser. Formally checked paths in prover input are subject to completion (\secref{sec:completion}): partial specifications are resolved via directory content and possible completions are offered in a popup. \ subsection \PIDE resources via virtual file-systems\ text \ The jEdit file browser is docked by default. It provides immediate access to the local file-system, as well as important Isabelle resources via the \<^emph>\Favorites\ menu. Environment variables like \<^verbatim>\$ISABELLE_HOME\ are discussed in \secref{sec:local-files}. Virtual file-systems are more special: the idea is to present structured information like a directory tree. The following URLs are offered in the \<^emph>\Favorites\ menu, or by corresponding jEdit actions. \<^item> URL \<^verbatim>\isabelle-export:\ or action @{action_def "isabelle-export-browser"} shows a toplevel directory with theory names: each may provide its own tree structure of session exports. Exports are like a logical file-system for the current prover session, maintained as Isabelle/Scala data structures and written to the session database eventually. The \<^verbatim>\isabelle-export:\ URL exposes the current content according to a snapshot of the document model. The file browser is \<^emph>\not\ updated continuously when the PIDE document changes: the reload operation needs to be used explicitly. A notable example for exports is the command @{command_ref export_code} @{cite "isabelle-isar-ref"}. \<^item> URL \<^verbatim>\isabelle-session:\ or action @{action_def "isabelle-session-browser"} show the structure of session chapters and sessions within them. What looks like a file-entry is actually a reference to the session definition in its corresponding \<^verbatim>\ROOT\ file. The latter is subject to Prover IDE markup, so the session theories and other files may be browsed quickly by following hyperlinks in the text. \ section \Indentation\ text \ Isabelle/jEdit augments the existing indentation facilities of jEdit to take the structure of theory and proof texts into account. There is also special support for unstructured proof scripts (\<^theory_text>\apply\ etc.). \<^descr>[Syntactic indentation] follows the outer syntax of Isabelle/Isar. Action @{action "indent-lines"} (shortcut \<^verbatim>\C+i\) indents the current line according to command keywords and some command substructure: this approximation may need further manual tuning. Action @{action "isabelle.newline"} (shortcut \<^verbatim>\ENTER\) indents the old and the new line according to command keywords only: leading to precise alignment of the main Isar language elements. This depends on option @{system_option_def "jedit_indent_newline"} (enabled by default). Regular input (via keyboard or completion) indents the current line whenever an new keyword is emerging at the start of the line. This depends on option @{system_option_def "jedit_indent_input"} (enabled by default). \<^descr>[Semantic indentation] adds additional white space to unstructured proof scripts via the number of subgoals. This requires information of ongoing document processing and may thus lag behind when the user is editing too quickly; see also option @{system_option_def "jedit_script_indent"} and @{system_option_def "jedit_script_indent_limit"}. The above options are accessible in the menu \<^emph>\Plugins / Plugin Options / Isabelle / General\. A prerequisite for advanced indentation is \<^emph>\Utilities / Buffer Options / Automatic indentation\: it needs to be set to \<^verbatim>\full\ (default). \ section \SideKick parsers \label{sec:sidekick}\ text \ The \<^emph>\SideKick\ plugin provides some general services to display buffer structure in a tree view. Isabelle/jEdit provides SideKick parsers for its main mode for theory files, ML files, as well as some minor modes for the \<^verbatim>\NEWS\ file (see \figref{fig:sidekick}), session \<^verbatim>\ROOT\ files, system \<^verbatim>\options\, and Bib{\TeX} files (\secref{sec:bibtex}). \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{sidekick} \end{center} \caption{The Isabelle NEWS file with SideKick tree view} \label{fig:sidekick} \end{figure} The default SideKick parser for theory files is \<^verbatim>\isabelle\: it provides a tree-view on the formal document structure, with section headings at the top and formal specification elements at the bottom. The alternative parser \<^verbatim>\isabelle-context\ shows nesting of context blocks according to \<^theory_text>\begin \ end\ structure. \<^medskip> Isabelle/ML files are structured according to semi-formal comments that are explained in @{cite "isabelle-implementation"}. This outline is turned into a tree-view by default, by using the \<^verbatim>\isabelle-ml\ parser. There is also a folding mode of the same name, for hierarchic text folds within ML files. \<^medskip> The special SideKick parser \<^verbatim>\isabelle-markup\ exposes the uninterpreted markup tree of the PIDE document model of the current buffer. This is occasionally useful for informative purposes, but the amount of displayed information might cause problems for large buffers. \ chapter \Prover IDE functionality \label{sec:document-model}\ section \Document model \label{sec:document-model}\ text \ The document model is central to the PIDE architecture: the editor and the prover have a common notion of structured source text with markup, which is produced by formal processing. The editor is responsible for edits of document source, as produced by the user. The prover is responsible for reports of document markup, as produced by its processing in the background. Isabelle/jEdit handles classic editor events of jEdit, in order to connect the physical world of the GUI (with its singleton state) to the mathematical world of multiple document versions (with timeless and stateless updates). \ subsection \Editor buffers and document nodes \label{sec:buffer-node}\ text \ As a regular text editor, jEdit maintains a collection of \<^emph>\buffers\ to store text files; each buffer may be associated with any number of visible \<^emph>\text areas\. Buffers are subject to an \<^emph>\edit mode\ that is determined from the file name extension. The following modes are treated specifically in Isabelle/jEdit: \<^medskip> \begin{tabular}{lll} \<^bold>\mode\ & \<^bold>\file name\ & \<^bold>\content\ \\\hline \<^verbatim>\isabelle\ & \<^verbatim>\*.thy\ & theory source \\ \<^verbatim>\isabelle-ml\ & \<^verbatim>\*.ML\ & Isabelle/ML source \\ \<^verbatim>\sml\ & \<^verbatim>\*.sml\ or \<^verbatim>\*.sig\ & Standard ML source \\ \<^verbatim>\isabelle-root\ & \<^verbatim>\ROOT\ & session root \\ \<^verbatim>\isabelle-options\ & & Isabelle options \\ \<^verbatim>\isabelle-news\ & & Isabelle NEWS \\ \end{tabular} \<^medskip> All jEdit buffers are automatically added to the PIDE document-model as \<^emph>\document nodes\. The overall document structure is defined by the theory nodes in two dimensions: \<^enum> via \<^bold>\theory imports\ that are specified in the \<^emph>\theory header\ using concrete syntax of the @{command_ref theory} command @{cite "isabelle-isar-ref"}; \<^enum> via \<^bold>\auxiliary files\ that are included into a theory by \<^emph>\load commands\, notably @{command_ref ML_file} and @{command_ref SML_file} @{cite "isabelle-isar-ref"}. In any case, source files are managed by the PIDE infrastructure: the physical file-system only plays a subordinate role. The relevant version of source text is passed directly from the editor to the prover, using internal communication channels. \ subsection \Theories \label{sec:theories}\ text \ The \<^emph>\Theories\ panel (see also \figref{fig:theories}) provides an overview of the status of continuous checking of theory nodes within the document model. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{theories} \end{center} \caption{Theories panel with an overview of the document-model, and jEdit text areas as editable views on some of the document nodes} \label{fig:theories} \end{figure} Theory imports are resolved automatically by the PIDE document model: all required files are loaded and stored internally, without the need to open corresponding jEdit buffers. Opening or closing editor buffers later on has no direct impact on the formal document content: it only affects visibility. In contrast, auxiliary files (e.g.\ from @{command ML_file} commands) are \<^emph>\not\ resolved within the editor by default, but the prover process takes care of that. This may be changed by enabling the system option @{system_option jedit_auto_resolve}: it ensures that all files are uniformly provided by the editor. \<^medskip> The visible \<^emph>\perspective\ of Isabelle/jEdit is defined by the collective view on theory buffers via open text areas. The perspective is taken as a hint for document processing: the prover ensures that those parts of a theory where the user is looking are checked, while other parts that are presently not required are ignored. The perspective is changed by opening or closing text area windows, or scrolling within a window. The \<^emph>\Theories\ panel provides some further options to influence the process of continuous checking: it may be switched off globally to restrict the prover to superficial processing of command syntax. It is also possible to indicate theory nodes as \<^emph>\required\ for continuous checking: this means such nodes and all their imports are always processed independently of the visibility status (if continuous checking is enabled). Big theory libraries that are marked as required can have significant impact on performance! The \<^emph>\Purge\ button restricts the document model to theories that are required for open editor buffers: inaccessible theories are removed and will be rechecked when opened or imported later. \<^medskip> Formal markup of checked theory content is turned into GUI rendering, based on a standard repertoire known from mainstream IDEs for programming languages: colors, icons, highlighting, squiggly underlines, tooltips, hyperlinks etc. For outer syntax of Isabelle/Isar there is some traditional syntax-highlighting via static keywords and tokenization within the editor; this buffer syntax is determined from theory imports. In contrast, the painting of inner syntax (term language etc.)\ uses semantic information that is reported dynamically from the logical context. Thus the prover can provide additional markup to help the user to understand the meaning of formal text, and to produce more text with some add-on tools (e.g.\ information messages with \<^emph>\sendback\ markup by automated provers or disprovers in the background). \ subsection \Auxiliary files \label{sec:aux-files}\ text \ Special load commands like @{command_ref ML_file} and @{command_ref SML_file} @{cite "isabelle-isar-ref"} refer to auxiliary files within some theory. Conceptually, the file argument of the command extends the theory source by the content of the file, but its editor buffer may be loaded~/ changed~/ saved separately. The PIDE document model propagates changes of auxiliary file content to the corresponding load command in the theory, to update and process it accordingly: changes of auxiliary file content are treated as changes of the corresponding load command. \<^medskip> As a concession to the massive amount of ML files in Isabelle/HOL itself, the content of auxiliary files is only added to the PIDE document-model on demand, the first time when opened explicitly in the editor. There are further tricks to manage markup of ML files, such that Isabelle/HOL may be edited conveniently in the Prover IDE on small machines with only 8\,GB of main memory. Using \<^verbatim>\Pure\ as logic session image, the exploration may start at the top \<^file>\$ISABELLE_HOME/src/HOL/Main.thy\ or the bottom \<^file>\$ISABELLE_HOME/src/HOL/HOL.thy\, for example. It is also possible to explore the Isabelle/Pure bootstrap process (a virtual copy) by opening \<^file>\$ISABELLE_HOME/src/Pure/ROOT.ML\ like a theory in the Prover IDE. Initially, before an auxiliary file is opened in the editor, the prover reads its content from the physical file-system. After the file is opened for the first time in the editor, e.g.\ by following the hyperlink (\secref{sec:tooltips-hyperlinks}) for the argument of its @{command ML_file} command, the content is taken from the jEdit buffer. The change of responsibility from prover to editor counts as an update of the document content, so subsequent theory sources need to be re-checked. When the buffer is closed, the responsibility remains to the editor: the file may be opened again without causing another document update. A file that is opened in the editor, but its theory with the load command is not, is presently inactive in the document model. A file that is loaded via multiple load commands is associated to an arbitrary one: this situation is morally unsupported and might lead to confusion. \<^medskip> Output that refers to an auxiliary file is combined with that of the corresponding load command, and shown whenever the file or the command are active (see also \secref{sec:output}). Warnings, errors, and other useful markup is attached directly to the positions in the auxiliary file buffer, in the manner of standard IDEs. By using the load command @{command SML_file} as explained in \<^file>\$ISABELLE_HOME/src/Tools/SML/Examples.thy\, Isabelle/jEdit may be used as fully-featured IDE for Standard ML, independently of theory or proof development: the required theory merely serves as some kind of project file for a collection of SML source modules. \ section \Output \label{sec:output}\ text \ Prover output consists of \<^emph>\markup\ and \<^emph>\messages\. Both are directly attached to the corresponding positions in the original source text, and visualized in the text area, e.g.\ as text colours for free and bound variables, or as squiggly underlines for warnings, errors etc.\ (see also \figref{fig:output}). In the latter case, the corresponding messages are shown by hovering with the mouse over the highlighted text --- although in many situations the user should already get some clue by looking at the position of the text highlighting, without seeing the message body itself. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{output} \end{center} \caption{Multiple views on prover output: gutter with icon, text area with popup, text overview column, \<^emph>\Theories\ panel, \<^emph>\Output\ panel} \label{fig:output} \end{figure} The ``gutter'' on the left-hand-side of the text area uses icons to provide a summary of the messages within the adjacent text line. Message priorities are used to prefer errors over warnings, warnings over information messages; other output is ignored. The ``text overview column'' on the right-hand-side of the text area uses similar information to paint small rectangles for the overall status of the whole text buffer. The graphics is scaled to fit the logical buffer length into the given window height. Mouse clicks on the overview area move the cursor approximately to the corresponding text line in the buffer. The \<^emph>\Theories\ panel provides another course-grained overview, but without direct correspondence to text positions. The coloured rectangles represent the amount of messages of a certain kind (warnings, errors, etc.) and the execution status of commands. The border of each rectangle indicates the overall status of processing: a thick border means it is \<^emph>\finished\ or \<^emph>\failed\ (with color for errors). A double-click on one of the theory entries with their status overview opens the corresponding text buffer, without moving the cursor to a specific point. \<^medskip> The \<^emph>\Output\ panel displays prover messages that correspond to a given command, within a separate window. The cursor position in the presently active text area determines the prover command whose cumulative message output is appended and shown in that window (in canonical order according to the internal execution of the command). There are also control elements to modify the update policy of the output wrt.\ continued editor movements: \<^emph>\Auto update\ and \<^emph>\Update\. This is particularly useful for multiple instances of the \<^emph>\Output\ panel to look at different situations. Alternatively, the panel can be turned into a passive \<^emph>\Info\ window via the \<^emph>\Detach\ menu item. Proof state is handled separately (\secref{sec:state-output}), but it is also possible to tick the corresponding checkbox to append it to regular output (\figref{fig:output-including-state}). This is a globally persistent option: it affects all open panels and future editor sessions. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{output-including-state} \end{center} \caption{Proof state display within the regular output panel} \label{fig:output-including-state} \end{figure} \<^medskip> Following the IDE principle, regular messages are attached to the original source in the proper place and may be inspected on demand via popups. This excludes messages that are somehow internal to the machinery of proof checking, notably \<^emph>\proof state\ and \<^emph>\tracing\. In any case, the same display technology is used for small popups and big output windows. The formal text contains markup that may be explored recursively via further popups and hyperlinks (see \secref{sec:tooltips-hyperlinks}), or clicked directly to initiate certain actions (see \secref{sec:auto-tools} and \secref{sec:sledgehammer}). \<^medskip> Alternatively, the subsequent actions (with keyboard shortcuts) allow to show tooltip messages or navigate error positions: \<^medskip> \begin{tabular}[t]{l} @{action_ref "isabelle.tooltip"} (\<^verbatim>\CS+b\) \\ @{action_ref "isabelle.message"} (\<^verbatim>\CS+m\) \\ \end{tabular}\quad \begin{tabular}[t]{l} @{action_ref "isabelle.first-error"} (\<^verbatim>\CS+a\) \\ @{action_ref "isabelle.last-error"} (\<^verbatim>\CS+z\) \\ @{action_ref "isabelle.next-error"} (\<^verbatim>\CS+n\) \\ @{action_ref "isabelle.prev-error"} (\<^verbatim>\CS+p\) \\ \end{tabular} \<^medskip> \ section \Proof state \label{sec:state-output}\ text \ The main purpose of the Prover IDE is to help the user editing proof documents, with ongoing formal checking by the prover in the background. This can be done to some extent in the main text area alone, especially for well-structured Isar proofs. Nonetheless, internal proof state needs to be inspected in many situations of exploration and ``debugging''. The \<^emph>\State\ panel shows exclusively such proof state messages without further distraction, while all other messages are displayed in \<^emph>\Output\ (\secref{sec:output}). \Figref{fig:output-and-state} shows a typical GUI layout where both panels are open. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{output-and-state} \end{center} \caption{Separate proof state display (right) and other output (bottom).} \label{fig:output-and-state} \end{figure} Another typical arrangement has more than one \<^emph>\State\ panel open (as floating windows), with \<^emph>\Auto update\ disabled to look at an old situation while the proof text in the vicinity is changed. The \<^emph>\Update\ button triggers an explicit one-shot update; this operation is also available via the action @{action "isabelle.update-state"} (keyboard shortcut \<^verbatim>\S+ENTER\). On small screens, it is occasionally useful to have all messages concatenated in the regular \<^emph>\Output\ panel, e.g.\ see \figref{fig:output-including-state}. \<^medskip> The mechanics of \<^emph>\Output\ versus \<^emph>\State\ are slightly different: \<^item> \<^emph>\Output\ shows information that is continuously produced and already present when the GUI wants to show it. This is implicitly controlled by the visible perspective on the text. \<^item> \<^emph>\State\ initiates a real-time query on demand, with a full round trip including a fresh print operation on the prover side. This is controlled explicitly when the cursor is moved to the next command (\<^emph>\Auto update\) or the \<^emph>\Update\ operation is triggered. This can make a difference in GUI responsibility and resource usage within the prover process. Applications with very big proof states that are only inspected in isolation work better with the \<^emph>\State\ panel. \ section \Query \label{sec:query}\ text \ The \<^emph>\Query\ panel provides various GUI forms to request extra information from the prover, as a replacement of old-style diagnostic commands like @{command find_theorems}. There are input fields and buttons for a particular query command, with output in a dedicated text area. The main query modes are presented as separate tabs: \<^emph>\Find Theorems\, \<^emph>\Find Constants\, \<^emph>\Print Context\, e.g.\ see \figref{fig:query}. As usual in jEdit, multiple \<^emph>\Query\ windows may be active at the same time: any number of floating instances, but at most one docked instance (which is used by default). \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{query} \end{center} \caption{An instance of the Query panel: find theorems} \label{fig:query} \end{figure} \<^medskip> The following GUI elements are common to all query modes: \<^item> The spinning wheel provides feedback about the status of a pending query wrt.\ the evaluation of its context and its own operation. \<^item> The \<^emph>\Apply\ button attaches a fresh query invocation to the current context of the command where the cursor is pointing in the text. \<^item> The \<^emph>\Search\ field allows to highlight query output according to some regular expression, in the notation that is commonly used on the Java platform.\<^footnote>\\<^url>\https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/regex/Pattern.html\\ This may serve as an additional visual filter of the result. \<^item> The \<^emph>\Zoom\ box controls the font size of the output area. All query operations are asynchronous: there is no need to wait for the evaluation of the document for the query context, nor for the query operation itself. Query output may be detached as independent \<^emph>\Info\ window, using a menu operation of the dockable window manager. The printed result usually provides sufficient clues about the original query, with some hyperlink to its context (via markup of its head line). \ subsection \Find theorems\ text \ The \<^emph>\Query\ panel in \<^emph>\Find Theorems\ mode retrieves facts from the theory or proof context matching all of given criteria in the \<^emph>\Find\ text field. A single criterion has the following syntax: \<^rail>\ ('-'?) ('name' ':' @{syntax name} | 'intro' | 'elim' | 'dest' | 'solves' | 'simp' ':' @{syntax term} | @{syntax term}) \ See also the Isar command @{command_ref find_theorems} in @{cite "isabelle-isar-ref"}. \ subsection \Find constants\ text \ The \<^emph>\Query\ panel in \<^emph>\Find Constants\ mode prints all constants whose type meets all of the given criteria in the \<^emph>\Find\ text field. A single criterion has the following syntax: \<^rail>\ ('-'?) ('name' ':' @{syntax name} | 'strict' ':' @{syntax type} | @{syntax type}) \ See also the Isar command @{command_ref find_consts} in @{cite "isabelle-isar-ref"}. \ subsection \Print context\ text \ The \<^emph>\Query\ panel in \<^emph>\Print Context\ mode prints information from the theory or proof context, or proof state. See also the Isar commands @{command_ref print_context}, @{command_ref print_cases}, @{command_ref print_term_bindings}, @{command_ref print_theorems}, described in @{cite "isabelle-isar-ref"}. \ section \Tooltips and hyperlinks \label{sec:tooltips-hyperlinks}\ text \ Formally processed text (prover input or output) contains rich markup that can be explored by using the \<^verbatim>\CONTROL\ modifier key on Linux and Windows, or \<^verbatim>\COMMAND\ on macOS. Hovering with the mouse while the modifier is pressed reveals a \<^emph>\tooltip\ (grey box over the text with a yellow popup) and/or a \<^emph>\hyperlink\ (black rectangle over the text with change of mouse pointer); see also \figref{fig:tooltip}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{popup1} \end{center} \caption{Tooltip and hyperlink for some formal entity} \label{fig:tooltip} \end{figure} Tooltip popups use the same rendering technology as the main text area, and further tooltips and/or hyperlinks may be exposed recursively by the same mechanism; see \figref{fig:nested-tooltips}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{popup2} \end{center} \caption{Nested tooltips over formal entities} \label{fig:nested-tooltips} \end{figure} The tooltip popup window provides some controls to \<^emph>\close\ or \<^emph>\detach\ the window, turning it into a separate \<^emph>\Info\ window managed by jEdit. The \<^verbatim>\ESCAPE\ key closes \<^emph>\all\ popups, which is particularly relevant when nested tooltips are stacking up. \<^medskip> A black rectangle in the text indicates a hyperlink that may be followed by a mouse click (while the \<^verbatim>\CONTROL\ or \<^verbatim>\COMMAND\ modifier key is still pressed). Such jumps to other text locations are recorded by the \<^emph>\Navigator\ plugin, which is bundled with Isabelle/jEdit and enabled by default. There are usually navigation arrows in the main jEdit toolbar. Note that the link target may be a file that is itself not subject to formal document processing of the editor session and thus prevents further exploration: the chain of hyperlinks may end in some source file of the underlying logic image, or within the ML bootstrap sources of Isabelle/Pure. \ section \Formal scopes and semantic selection\ text \ Formal entities are semantically annotated in the source text as explained in \secref{sec:tooltips-hyperlinks}. A \<^emph>\formal scope\ consists of the defining position with all its referencing positions. This correspondence is highlighted in the text according to the cursor position, see also \figref{fig:scope1}. Here the referencing positions are rendered with an additional border, in reminiscence to a hyperlink. A mouse click with \<^verbatim>\C\ modifier, or the action @{action_def "isabelle.goto-entity"} (shortcut \<^verbatim>\CS+d\) jumps to the original defining position. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{scope1} \end{center} \caption{Scope of formal entity: defining vs.\ referencing positions} \label{fig:scope1} \end{figure} The action @{action_def "isabelle.select-entity"} (shortcut \<^verbatim>\CS+ENTER\) supports semantic selection of all occurrences of the formal entity at the caret position, with a defining position in the current editor buffer. This facilitates systematic renaming, using regular jEdit editing of a multi-selection, see also \figref{fig:scope2}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{scope2} \end{center} \caption{The result of semantic selection and systematic renaming} \label{fig:scope2} \end{figure} By default, the visual feedback on scopes is restricted to definitions within the visible text area. The keyboard modifier \<^verbatim>\CS\ overrides this: then all defining and referencing positions are shown. This modifier may be configured via option @{system_option jedit_focus_modifier}; the default coincides with the modifier for the above keyboard actions. The empty string means to disable this additional visual feedback. \ section \Completion \label{sec:completion}\ text \ Smart completion of partial input is the IDE functionality \<^emph>\par excellance\. Isabelle/jEdit combines several sources of information to achieve that. Despite its complexity, it should be possible to get some idea how completion works by experimentation, based on the overview of completion varieties in \secref{sec:completion-varieties}. The remaining subsections explain concepts around completion more systematically. \<^medskip> \<^emph>\Explicit completion\ is triggered by the action @{action_ref "isabelle.complete"}, which is bound to the keyboard shortcut \<^verbatim>\C+b\, and thus overrides the jEdit default for @{action_ref "complete-word"}. \<^emph>\Implicit completion\ hooks into the regular keyboard input stream of the editor, with some event filtering and optional delays. \<^medskip> Completion options may be configured in \<^emph>\Plugin Options~/ Isabelle~/ General~/ Completion\. These are explained in further detail below, whenever relevant. There is also a summary of options in \secref{sec:completion-options}. The asynchronous nature of PIDE interaction means that information from the prover is delayed --- at least by a full round-trip of the document update protocol. The default options already take this into account, with a sufficiently long completion delay to speculate on the availability of all relevant information from the editor and the prover, before completing text immediately or producing a popup. Although there is an inherent danger of non-deterministic behaviour due to such real-time parameters, the general completion policy aims at determined results as far as possible. \ subsection \Varieties of completion \label{sec:completion-varieties}\ subsubsection \Built-in templates\ text \ Isabelle is ultimately a framework of nested sub-languages of different kinds and purposes. The completion mechanism supports this by the following built-in templates: \<^descr> \<^verbatim>\`\ (single ASCII back-quote) or \<^verbatim>\"\ (double ASCII quote) support \<^emph>\quotations\ via text cartouches. There are three selections, which are always presented in the same order and do not depend on any context information. The default choice produces a template ``\\\\\'', where the box indicates the cursor position after insertion; the other choices help to repair the block structure of unbalanced text cartouches. \<^descr> \<^verbatim>\@{\ is completed to the template ``\@{\}\'', where the box indicates the cursor position after insertion. Here it is convenient to use the wildcard ``\<^verbatim>\__\'' or a more specific name prefix to let semantic completion of name-space entries propose antiquotation names. With some practice, input of quoted sub-languages and antiquotations of embedded languages should work smoothly. Note that national keyboard layouts might cause problems with back-quote as dead key, but double quote can be used instead. \ subsubsection \Syntax keywords\ text \ Syntax completion tables are determined statically from the keywords of the ``outer syntax'' of the underlying edit mode: for theory files this is the syntax of Isar commands according to the cumulative theory imports. Keywords are usually plain words, which means the completion mechanism only inserts them directly into the text for explicit completion (\secref{sec:completion-input}), but produces a popup (\secref{sec:completion-popup}) otherwise. At the point where outer syntax keywords are defined, it is possible to specify an alternative replacement string to be inserted instead of the keyword itself. An empty string means to suppress the keyword altogether, which is occasionally useful to avoid confusion, e.g.\ the rare keyword @{command simproc_setup} vs.\ the frequent name-space entry \simp\. \ subsubsection \Isabelle symbols\ text \ The completion tables for Isabelle symbols (\secref{sec:symbols}) are determined statically from \<^file>\$ISABELLE_HOME/etc/symbols\ and \<^path>\$ISABELLE_HOME_USER/etc/symbols\ for each symbol specification as follows: \<^medskip> \begin{tabular}{ll} \<^bold>\completion entry\ & \<^bold>\example\ \\\hline literal symbol & \<^verbatim>\\\ \\ symbol name with backslash & \<^verbatim>\\\\<^verbatim>\forall\ \\ symbol abbreviation & \<^verbatim>\ALL\ or \<^verbatim>\!\ \\ \end{tabular} \<^medskip> When inserted into the text, the above examples all produce the same Unicode rendering \\\ of the underlying symbol \<^verbatim>\\\. A symbol abbreviation that is a plain word, like \<^verbatim>\ALL\, is treated like a syntax keyword. Non-word abbreviations like \<^verbatim>\-->\ are inserted more aggressively, except for single-character abbreviations like \<^verbatim>\!\ above. Completion via abbreviations like \<^verbatim>\ALL\ or \<^verbatim>\-->\ depends on the semantic language context (\secref{sec:completion-context}). In contrast, backslash sequences like \<^verbatim>\\forall\ \<^verbatim>\\\ are always possible, but require additional interaction to confirm (via popup). This is important in ambiguous situations, e.g.\ for Isabelle document source, which may contain formal symbols or informal {\LaTeX} macros. Backslash sequences also help when input is broken, and thus escapes its normal semantic context: e.g.\ antiquotations or string literals in ML, which do not allow arbitrary backslash sequences. Special symbols like \<^verbatim>\\\ or control symbols like \<^verbatim>\\<^cancel>\, \<^verbatim>\\<^latex>\, \<^verbatim>\\<^binding>\ can have an argument: completing on a name prefix offers a template with an empty cartouche. Thus completion of \<^verbatim>\\co\ or \<^verbatim>\\ca\ allows to compose formal document comments quickly.\<^footnote>\It is customary to put a space between \<^verbatim>\\\ and its argument, while control symbols do \<^emph>\not\ allow extra space here.\ \ subsubsection \User-defined abbreviations\ text \ The theory header syntax supports abbreviations via the \<^theory_text>\abbrevs\ keyword @{cite "isabelle-isar-ref"}. This is a slight generalization of built-in templates and abbreviations for Isabelle symbols, as explained above. Examples may be found in the Isabelle sources, by searching for ``\<^verbatim>\abbrevs\'' in \<^verbatim>\*.thy\ files. The \<^emph>\Symbols\ panel shows the abbreviations that are available in the current theory buffer (according to its \<^theory_text>\imports\) in the \<^verbatim>\Abbrevs\ tab. \ subsubsection \Name-space entries\ text \ This is genuine semantic completion, using information from the prover, so it requires some delay. A \<^emph>\failed name-space lookup\ produces an error message that is annotated with a list of alternative names that are legal. The list of results is truncated according to the system option @{system_option_ref completion_limit}. The completion mechanism takes this into account when collecting information on the prover side. Already recognized names are \<^emph>\not\ completed further, but completion may be extended by appending a suffix of underscores. This provokes a failed lookup, and another completion attempt (ignoring the underscores). For example, in a name space where \<^verbatim>\foo\ and \<^verbatim>\foobar\ are known, the input \<^verbatim>\foo\ remains unchanged, but \<^verbatim>\foo_\ may be completed to \<^verbatim>\foo\ or \<^verbatim>\foobar\. The special identifier ``\<^verbatim>\__\'' serves as a wild-card for arbitrary completion: it exposes the name-space content to the completion mechanism (truncated according to @{system_option completion_limit}). This is occasionally useful to explore an unknown name-space, e.g.\ in some template. \ subsubsection \File-system paths\ text \ Depending on prover markup about file-system paths in the source text, e.g.\ for the argument of a load command (\secref{sec:aux-files}), the completion mechanism explores the directory content and offers the result as completion popup. Relative path specifications are understood wrt.\ the \<^emph>\master directory\ of the document node (\secref{sec:buffer-node}) of the enclosing editor buffer; this requires a proper theory, not an auxiliary file. A suffix of slashes may be used to continue the exploration of an already recognized directory name. \ subsubsection \Spell-checking\ text \ The spell-checker combines semantic markup from the prover (regions of plain words) with static dictionaries (word lists) that are known to the editor. Unknown words are underlined in the text, using @{system_option_ref spell_checker_color} (blue by default). This is not an error, but a hint to the user that some action may be taken. The jEdit context menu provides various actions, as far as applicable: \<^medskip> \begin{tabular}{l} @{action_ref "isabelle.complete-word"} \\ @{action_ref "isabelle.exclude-word"} \\ @{action_ref "isabelle.exclude-word-permanently"} \\ @{action_ref "isabelle.include-word"} \\ @{action_ref "isabelle.include-word-permanently"} \\ \end{tabular} \<^medskip> Instead of the specific @{action_ref "isabelle.complete-word"}, it is also possible to use the generic @{action_ref "isabelle.complete"} with its default keyboard shortcut \<^verbatim>\C+b\. \<^medskip> Dictionary lookup uses some educated guesses about lower-case, upper-case, and capitalized words. This is oriented on common use in English, where this aspect is not decisive for proper spelling (in contrast to German, for example). \ subsection \Semantic completion context \label{sec:completion-context}\ text \ Completion depends on a semantic context that is provided by the prover, although with some delay, because at least a full PIDE protocol round-trip is required. Until that information becomes available in the PIDE document-model, the default context is given by the outer syntax of the editor mode (see also \secref{sec:buffer-node}). The semantic \<^emph>\language context\ provides information about nested sub-languages of Isabelle: keywords are only completed for outer syntax, and antiquotations for languages that support them. Symbol abbreviations only work for specific sub-languages: e.g.\ ``\<^verbatim>\=>\'' is \<^emph>\not\ completed in regular ML source, but is completed within ML strings, comments, antiquotations. Backslash representations of symbols like ``\<^verbatim>\\foobar\'' or ``\<^verbatim>\\\'' work in any context --- after additional confirmation. The prover may produce \<^emph>\no completion\ markup in exceptional situations, to tell that some language keywords should be excluded from further completion attempts. For example, ``\<^verbatim>\:\'' within accepted Isar syntax looses its meaning as abbreviation for symbol ``\\\''. \ subsection \Input events \label{sec:completion-input}\ text \ Completion is triggered by certain events produced by the user, with optional delay after keyboard input according to @{system_option jedit_completion_delay}. \<^descr>[Explicit completion] works via action @{action_ref "isabelle.complete"} with keyboard shortcut \<^verbatim>\C+b\. This overrides the shortcut for @{action_ref "complete-word"} in jEdit, but it is possible to restore the original jEdit keyboard mapping of @{action "complete-word"} via \<^emph>\Global Options~/ Shortcuts\ and invent a different one for @{action "isabelle.complete"}. \<^descr>[Explicit spell-checker completion] works via @{action_ref "isabelle.complete-word"}, which is exposed in the jEdit context menu, if the mouse points to a word that the spell-checker can complete. \<^descr>[Implicit completion] works via regular keyboard input of the editor. It depends on further side-conditions: \<^enum> The system option @{system_option_ref jedit_completion} needs to be enabled (default). \<^enum> Completion of syntax keywords requires at least 3 relevant characters in the text. \<^enum> The system option @{system_option_ref jedit_completion_delay} determines an additional delay (0.5 by default), before opening a completion popup. The delay gives the prover a chance to provide semantic completion information, notably the context (\secref{sec:completion-context}). \<^enum> The system option @{system_option_ref jedit_completion_immediate} (enabled by default) controls whether replacement text should be inserted immediately without popup, regardless of @{system_option jedit_completion_delay}. This aggressive mode of completion is restricted to symbol abbreviations that are not plain words (\secref{sec:symbols}). \<^enum> Completion of symbol abbreviations with only one relevant character in the text always enforces an explicit popup, regardless of @{system_option_ref jedit_completion_immediate}. \ subsection \Completion popup \label{sec:completion-popup}\ text \ A \<^emph>\completion popup\ is a minimally invasive GUI component over the text area that offers a selection of completion items to be inserted into the text, e.g.\ by mouse clicks. Items are sorted dynamically, according to the frequency of selection, with persistent history. The popup may interpret special keys \<^verbatim>\ENTER\, \<^verbatim>\TAB\, \<^verbatim>\ESCAPE\, \<^verbatim>\UP\, \<^verbatim>\DOWN\, \<^verbatim>\PAGE_UP\, \<^verbatim>\PAGE_DOWN\, but all other key events are passed to the underlying text area. This allows to ignore unwanted completions most of the time and continue typing quickly. Thus the popup serves as a mechanism of confirmation of proposed items, while the default is to continue without completion. The meaning of special keys is as follows: \<^medskip> \begin{tabular}{ll} \<^bold>\key\ & \<^bold>\action\ \\\hline \<^verbatim>\ENTER\ & select completion (if @{system_option jedit_completion_select_enter}) \\ \<^verbatim>\TAB\ & select completion (if @{system_option jedit_completion_select_tab}) \\ \<^verbatim>\ESCAPE\ & dismiss popup \\ \<^verbatim>\UP\ & move up one item \\ \<^verbatim>\DOWN\ & move down one item \\ \<^verbatim>\PAGE_UP\ & move up one page of items \\ \<^verbatim>\PAGE_DOWN\ & move down one page of items \\ \end{tabular} \<^medskip> Movement within the popup is only active for multiple items. Otherwise the corresponding key event retains its standard meaning within the underlying text area. \ subsection \Insertion \label{sec:completion-insert}\ text \ Completion may first propose replacements to be selected (via a popup), or replace text immediately in certain situations and depending on certain options like @{system_option jedit_completion_immediate}. In any case, insertion works uniformly, by imitating normal jEdit text insertion, depending on the state of the \<^emph>\text selection\. Isabelle/jEdit tries to accommodate the most common forms of advanced selections in jEdit, but not all combinations make sense. At least the following important cases are well-defined: \<^descr>[No selection.] The original is removed and the replacement inserted, depending on the caret position. \<^descr>[Rectangular selection of zero width.] This special case is treated by jEdit as ``tall caret'' and insertion of completion imitates its normal behaviour: separate copies of the replacement are inserted for each line of the selection. \<^descr>[Other rectangular selection or multiple selections.] Here the original is removed and the replacement is inserted for each line (or segment) of the selection. Support for multiple selections is particularly useful for \<^emph>\HyperSearch\: clicking on one of the items in the \<^emph>\HyperSearch Results\ window makes jEdit select all its occurrences in the corresponding line of text. Then explicit completion can be invoked via \<^verbatim>\C+b\, e.g.\ to replace occurrences of \<^verbatim>\-->\ by \\\. \<^medskip> Insertion works by removing and inserting pieces of text from the buffer. This counts as one atomic operation on the jEdit history. Thus unintended completions may be reverted by the regular @{action undo} action of jEdit. According to normal jEdit policies, the recovered text after @{action undo} is selected: \<^verbatim>\ESCAPE\ is required to reset the selection and to continue typing more text. \ subsection \Options \label{sec:completion-options}\ text \ This is a summary of Isabelle/Scala system options that are relevant for completion. They may be configured in \<^emph>\Plugin Options~/ Isabelle~/ General\ as usual. \<^item> @{system_option_def completion_limit} specifies the maximum number of items for various semantic completion operations (name-space entries etc.) \<^item> @{system_option_def jedit_completion} guards implicit completion via regular jEdit key events (\secref{sec:completion-input}): it allows to disable implicit completion altogether. \<^item> @{system_option_def jedit_completion_select_enter} and @{system_option_def jedit_completion_select_tab} enable keys to select a completion item from the popup (\secref{sec:completion-popup}). Note that a regular mouse click on the list of items is always possible. \<^item> @{system_option_def jedit_completion_context} specifies whether the language context provided by the prover should be used at all. Disabling that option makes completion less ``semantic''. Note that incomplete or severely broken input may cause some disagreement of the prover and the user about the intended language context. \<^item> @{system_option_def jedit_completion_delay} and @{system_option_def jedit_completion_immediate} determine the handling of keyboard events for implicit completion (\secref{sec:completion-input}). A @{system_option jedit_completion_delay}~\<^verbatim>\> 0\ postpones the processing of key events, until after the user has stopped typing for the given time span, but @{system_option jedit_completion_immediate}~\<^verbatim>\= true\ means that abbreviations of Isabelle symbols are handled nonetheless. \<^item> @{system_option_def completion_path_ignore} specifies ``glob'' patterns to ignore in file-system path completion (separated by colons), e.g.\ backup files ending with tilde. \<^item> @{system_option_def spell_checker} is a global guard for all spell-checker operations: it allows to disable that mechanism altogether. \<^item> @{system_option_def spell_checker_dictionary} determines the current dictionary, taken from the colon-separated list in the settings variable @{setting_def JORTHO_DICTIONARIES}. There are jEdit actions to specify local updates to a dictionary, by including or excluding words. The result of permanent dictionary updates is stored in the directory \<^path>\$ISABELLE_HOME_USER/dictionaries\, in a separate file for each dictionary. \<^item> @{system_option_def spell_checker_include} specifies a comma-separated list of markup elements that delimit words in the source that is subject to spell-checking, including various forms of comments. \<^item> @{system_option_def spell_checker_exclude} specifies a comma-separated list of markup elements that disable spell-checking (e.g.\ in nested antiquotations). \ section \Automatically tried tools \label{sec:auto-tools}\ text \ Continuous document processing works asynchronously in the background. Visible document source that has been evaluated may get augmented by additional results of \<^emph>\asynchronous print functions\. An example for that is proof state output, if that is enabled in the Output panel (\secref{sec:output}). More heavy-weight print functions may be applied as well, e.g.\ to prove or disprove parts of the formal text by other means. Isabelle/HOL provides various automatically tried tools that operate on outermost goal statements (e.g.\ @{command lemma}, @{command theorem}), independently of the state of the current proof attempt. They work implicitly without any arguments. Results are output as \<^emph>\information messages\, which are indicated in the text area by blue squiggles and a blue information sign in the gutter (see \figref{fig:auto-tools}). The message content may be shown as for other output (see also \secref{sec:output}). Some tools produce output with \<^emph>\sendback\ markup, which means that clicking on certain parts of the text inserts that into the source in the proper place. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{auto-tools} \end{center} \caption{Result of automatically tried tools} \label{fig:auto-tools} \end{figure} \<^medskip> The following Isabelle system options control the behavior of automatically tried tools (see also the jEdit dialog window \<^emph>\Plugin Options~/ Isabelle~/ General~/ Automatically tried tools\): \<^item> @{system_option_ref auto_methods} controls automatic use of a combination of standard proof methods (@{method auto}, @{method simp}, @{method blast}, etc.). This corresponds to the Isar command @{command_ref "try0"} @{cite "isabelle-isar-ref"}. The tool is disabled by default, since unparameterized invocation of standard proof methods often consumes substantial CPU resources without leading to success. \<^item> @{system_option_ref auto_nitpick} controls a slightly reduced version of @{command_ref nitpick}, which tests for counterexamples using first-order relational logic. See also the Nitpick manual @{cite "isabelle-nitpick"}. This tool is disabled by default, due to the extra overhead of invoking an external Java process for each attempt to disprove a subgoal. \<^item> @{system_option_ref auto_quickcheck} controls automatic use of @{command_ref quickcheck}, which tests for counterexamples using a series of assignments for free variables of a subgoal. This tool is \<^emph>\enabled\ by default. It requires little overhead, but is a bit weaker than @{command nitpick}. \<^item> @{system_option_ref auto_sledgehammer} controls a significantly reduced version of @{command_ref sledgehammer}, which attempts to prove a subgoal using external automatic provers. See also the Sledgehammer manual @{cite "isabelle-sledgehammer"}. This tool is disabled by default, due to the relatively heavy nature of Sledgehammer. \<^item> @{system_option_ref auto_solve_direct} controls automatic use of @{command_ref solve_direct}, which checks whether the current subgoals can be solved directly by an existing theorem. This also helps to detect duplicate lemmas. This tool is \<^emph>\enabled\ by default. Invocation of automatically tried tools is subject to some global policies of parallel execution, which may be configured as follows: \<^item> @{system_option_ref auto_time_limit} (default 2.0) determines the timeout (in seconds) for each tool execution. \<^item> @{system_option_ref auto_time_start} (default 1.0) determines the start delay (in seconds) for automatically tried tools, after the main command evaluation is finished. Each tool is submitted independently to the pool of parallel execution tasks in Isabelle/ML, using hardwired priorities according to its relative ``heaviness''. The main stages of evaluation and printing of proof states take precedence, but an already running tool is not canceled and may thus reduce reactivity of proof document processing. Users should experiment how the available CPU resources (number of cores) are best invested to get additional feedback from prover in the background, by using a selection of weaker or stronger tools. \ section \Sledgehammer \label{sec:sledgehammer}\ text \ The \<^emph>\Sledgehammer\ panel (\figref{fig:sledgehammer}) provides a view on some independent execution of the Isar command @{command_ref sledgehammer}, with process indicator (spinning wheel) and GUI elements for important Sledgehammer arguments and options. Any number of Sledgehammer panels may be active, according to the standard policies of Dockable Window Management in jEdit. Closing such windows also cancels the corresponding prover tasks. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{sledgehammer} \end{center} \caption{An instance of the Sledgehammer panel} \label{fig:sledgehammer} \end{figure} The \<^emph>\Apply\ button attaches a fresh invocation of @{command sledgehammer} to the command where the cursor is pointing in the text --- this should be some pending proof problem. Further buttons like \<^emph>\Cancel\ and \<^emph>\Locate\ help to manage the running process. Results appear incrementally in the output window of the panel. Proposed proof snippets are marked-up as \<^emph>\sendback\, which means a single mouse click inserts the text into a suitable place of the original source. Some manual editing may be required nonetheless, say to remove earlier proof attempts. \ chapter \Isabelle document preparation\ text \ The ultimate purpose of Isabelle is to produce nicely rendered documents with the Isabelle document preparation system, which is based on {\LaTeX}; see also @{cite "isabelle-system" and "isabelle-isar-ref"}. Isabelle/jEdit provides some additional support for document editing. \ section \Document outline\ text \ Theory sources may contain document markup commands, such as @{command_ref chapter}, @{command_ref section}, @{command subsection}. The Isabelle SideKick parser (\secref{sec:sidekick}) represents this document outline as structured tree view, with formal statements and proofs nested inside; see \figref{fig:sidekick-document}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{sidekick-document} \end{center} \caption{Isabelle document outline via SideKick tree view} \label{fig:sidekick-document} \end{figure} It is also possible to use text folding according to this structure, by adjusting \<^emph>\Utilities / Buffer Options / Folding mode\ of jEdit. The default mode \<^verbatim>\isabelle\ uses the structure of formal definitions, statements, and proofs. The alternative mode \<^verbatim>\sidekick\ uses the document structure of the SideKick parser, as explained above. \ section \Markdown structure\ text \ Document text is internally structured in paragraphs and nested lists, using notation that is similar to Markdown\<^footnote>\\<^url>\https://commonmark.org\\. There are special control symbols for items of different kinds of lists, corresponding to \<^verbatim>\itemize\, \<^verbatim>\enumerate\, \<^verbatim>\description\ in {\LaTeX}. This is illustrated in for \<^verbatim>\itemize\ in \figref{fig:markdown-document}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{markdown-document} \end{center} \caption{Markdown structure within document text} \label{fig:markdown-document} \end{figure} Items take colour according to the depth of nested lists. This helps to explore the implicit rules for list structure interactively. There is also markup for individual items and paragraphs in the text: it may be explored via mouse hovering with \<^verbatim>\CONTROL\ / \<^verbatim>\COMMAND\ as usual (\secref{sec:tooltips-hyperlinks}). \ section \Citations and Bib{\TeX} entries \label{sec:bibtex}\ text \ Citations are managed by {\LaTeX} and Bib{\TeX} in \<^verbatim>\.bib\ files. The Isabelle session build process and the @{tool document} tool @{cite "isabelle-system"} are smart enough to assemble the result, based on the session directory layout. The document antiquotation \@{cite}\ is described in @{cite "isabelle-isar-ref"}. Within the Prover IDE it provides semantic markup for tooltips, hyperlinks, and completion for Bib{\TeX} database entries. Isabelle/jEdit does \<^emph>\not\ know about the actual Bib{\TeX} environment used in {\LaTeX} batch-mode, but it can take citations from those \<^verbatim>\.bib\ files that happen to be open in the editor; see \figref{fig:cite-completion}. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{cite-completion} \end{center} \caption{Semantic completion of citations from open Bib{\TeX} files} \label{fig:cite-completion} \end{figure} Isabelle/jEdit also provides IDE support for editing \<^verbatim>\.bib\ files themselves. There is syntax highlighting based on entry types (according to standard Bib{\TeX} styles), a context-menu to compose entries systematically, and a SideKick tree view of the overall content; see \figref{fig:bibtex-mode}. Semantic checking with errors and warnings is performed by the original \<^verbatim>\bibtex\ tool using style \<^verbatim>\plain\: different Bib{\TeX} styles may produce slightly different results. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{bibtex-mode} \end{center} \caption{Bib{\TeX} mode with context menu, SideKick tree view, and semantic output from the \<^verbatim>\bibtex\ tool} \label{fig:bibtex-mode} \end{figure} Regular document preview (\secref{sec:document-preview}) of \<^verbatim>\.bib\ files approximates the usual {\LaTeX} bibliography output in HTML (using style \<^verbatim>\unsort\). \ section \Document preview and printing \label{sec:document-preview}\ text \ The action @{action_def isabelle.preview} opens an HTML preview of the current document node in the default web browser. The content is derived from the semantic markup produced by the prover, and thus depends on the status of formal processing. Action @{action_def isabelle.draft} is similar to @{action isabelle.preview}, but shows a plain-text document draft. Both actions show document sources in a regular Web browser, which may be also used to print the result in a more portable manner than the Java printer dialog of the jEdit @{action_ref print} action. \ chapter \ML debugging within the Prover IDE\ text \ Isabelle/ML is based on Poly/ML\<^footnote>\\<^url>\https://www.polyml.org\\ and thus benefits from the source-level debugger of that implementation of Standard ML. The Prover IDE provides the \<^emph>\Debugger\ dockable to connect to running ML threads, inspect the stack frame with local ML bindings, and evaluate ML expressions in a particular run-time context. A typical debugger session is shown in \figref{fig:ml-debugger}. ML debugging depends on the following pre-requisites. \<^enum> ML source needs to be compiled with debugging enabled. This may be controlled for particular chunks of ML sources using any of the subsequent facilities. \<^enum> The system option @{system_option_ref ML_debugger} as implicit state of the Isabelle process. It may be changed in the menu \<^emph>\Plugins / Plugin Options / Isabelle / General\. ML modules need to be reloaded and recompiled to pick up that option as intended. \<^enum> The configuration option @{attribute_ref ML_debugger}, with an attribute of the same name, to update a global or local context (e.g.\ with the @{command declare} command). \<^enum> Commands that modify @{attribute ML_debugger} state for individual files: @{command_ref ML_file_debug}, @{command_ref ML_file_no_debug}, @{command_ref SML_file_debug}, @{command_ref SML_file_no_debug}. The instrumentation of ML code for debugging causes minor run-time overhead. ML modules that implement critical system infrastructure may lead to deadlocks or other undefined behaviour, when put under debugger control! \<^enum> The \<^emph>\Debugger\ panel needs to be active, otherwise the program ignores debugger instrumentation of the compiler and runs unmanaged. It is also possible to start debugging with the panel open, and later undock it, to let the program continue unhindered. \<^enum> The ML program needs to be stopped at a suitable breakpoint, which may be activated individually or globally as follows. For ML sources that have been compiled with debugger support, the IDE visualizes possible breakpoints in the text. A breakpoint may be toggled by pointing accurately with the mouse, with a right-click to activate jEdit's context menu and its \<^emph>\Toggle Breakpoint\ item. Alternatively, the \<^emph>\Break\ checkbox in the \<^emph>\Debugger\ panel may be enabled to stop ML threads always at the next possible breakpoint. Note that the state of individual breakpoints \<^emph>\gets lost\ when the coresponding ML source is re-compiled! This may happen unintentionally, e.g.\ when following hyperlinks into ML modules that have not been loaded into the IDE before. \begin{figure}[!htb] \begin{center} \includegraphics[scale=0.333]{ml-debugger} \end{center} \caption{ML debugger session} \label{fig:ml-debugger} \end{figure} The debugger panel (\figref{fig:ml-debugger}) shows a list of all threads that are presently stopped. Each thread shows a stack of all function invocations that lead to the current breakpoint at the top. It is possible to jump between stack positions freely, by clicking on this list. The current situation is displayed in the big output window, as a local ML environment with names and printed values. ML expressions may be evaluated in the current context by entering snippets of source into the text fields labeled \Context\ and \ML\, and pushing the \Eval\ button. By default, the source is interpreted as Isabelle/ML with the usual support for antiquotations (like @{command ML}, @{command ML_file}). Alternatively, strict Standard ML may be enforced via the \<^emph>\SML\ checkbox (like @{command SML_file}). The context for Isabelle/ML is optional, it may evaluate to a value of type \<^ML_type>\theory\, \<^ML_type>\Proof.context\, or \<^ML_type>\Context.generic\. Thus the given ML expression (with its antiquotations) may be subject to the intended dynamic run-time context, instead of the static compile-time context. \<^medskip> The buttons labeled \<^emph>\Continue\, \<^emph>\Step\, \<^emph>\Step over\, \<^emph>\Step out\ recommence execution of the program, with different policies concerning nested function invocations. The debugger always moves the cursor within the ML source to the next breakpoint position, and offers new stack frames as before. \ chapter \Miscellaneous tools\ section \Timing and monitoring\ text \ Managed evaluation of commands within PIDE documents includes timing information, which consists of elapsed (wall-clock) time, CPU time, and GC (garbage collection) time. Note that in a multithreaded system it is difficult to measure execution time precisely: elapsed time is closer to the real requirements of runtime resources than CPU or GC time, which are both subject to influences from the parallel environment that are outside the scope of the current command transaction. The \<^emph>\Timing\ panel provides an overview of cumulative command timings for each document node. Commands with elapsed time below the given threshold are ignored in the grand total. Nodes are sorted according to their overall timing. For the document node that corresponds to the current buffer, individual command timings are shown as well. A double-click on a theory node or command moves the editor focus to that particular source position. It is also possible to reveal individual timing information via some tooltip for the corresponding command keyword, using the technique of mouse hovering with \<^verbatim>\CONTROL\~/ \<^verbatim>\COMMAND\ modifier (\secref{sec:tooltips-hyperlinks}). Actual display of timing depends on the global option @{system_option_ref jedit_timing_threshold}, which can be configured in \<^emph>\Plugin Options~/ Isabelle~/ General\. \<^medskip> The jEdit status line includes a monitor widget for the current heap usage of the Isabelle/ML process; this includes information about ongoing garbage collection (shown as ``ML cleanup''). A double-click opens a new instance of the \<^emph>\Monitor\ panel, as explained below. There is a similar widget for the JVM: a double-click opens an external Java monitor process with detailed information and controls for the Java process underlying Isabelle/Scala/jEdit (this is based on \<^verbatim>\jconsole\). \<^medskip> The \<^emph>\Monitor\ panel visualizes various data collections about recent activity of the runtime system of Isabelle/ML and Java. There are buttons to request a full garbage collection and sharing of live data on the ML heap. The display is continuously updated according to @{system_option_ref editor_chart_delay}. Note that the painting of the chart takes considerable runtime itself --- on the Java Virtual Machine that runs Isabelle/Scala, not Isabelle/ML. \ section \Low-level output\ text \ Prover output is normally shown directly in the main text area or specific panels like \<^emph>\Output\ (\secref{sec:output}) or \<^emph>\State\ (\secref{sec:state-output}). Beyond this, it is occasionally useful to inspect low-level output channels via some of the following additional panels: \<^item> \<^emph>\Protocol\ shows internal messages between the Isabelle/Scala and Isabelle/ML side of the PIDE document editing protocol. Recording of messages starts with the first activation of the corresponding dockable window; earlier messages are lost. Display of protocol messages causes considerable slowdown, so it is important to undock all \<^emph>\Protocol\ panels for production work. \<^item> \<^emph>\Raw Output\ shows chunks of text from the \<^verbatim>\stdout\ and \<^verbatim>\stderr\ channels of the prover process. Recording of output starts with the first activation of the corresponding dockable window; earlier output is lost. The implicit stateful nature of physical I/O channels makes it difficult to relate raw output to the actual command from where it was originating. Parallel execution may add to the confusion. Peeking at physical process I/O is only the last resort to diagnose problems with tools that are not PIDE compliant. Under normal circumstances, prover output always works via managed message channels (corresponding to \<^ML>\writeln\, \<^ML>\warning\, \<^ML>\Output.error_message\ in Isabelle/ML), which are displayed by regular means within the document model (\secref{sec:output}). Unhandled Isabelle/ML exceptions are printed by the system via \<^ML>\Output.error_message\. \<^item> \<^emph>\Syslog\ shows system messages that might be relevant to diagnose problems with the startup or shutdown phase of the prover process; this also includes raw output on \<^verbatim>\stderr\. Isabelle/ML also provides an explicit \<^ML>\Output.system_message\ operation, which is occasionally useful for diagnostic purposes within the system infrastructure itself. A limited amount of syslog messages are buffered, independently of the docking state of the \<^emph>\Syslog\ panel. This allows to diagnose serious problems with Isabelle/PIDE process management, outside of the actual protocol layer. Under normal situations, such low-level system output can be ignored. \ chapter \Known problems and workarounds \label{sec:problems}\ text \ \<^item> \<^bold>\Problem:\ Keyboard shortcuts \<^verbatim>\C+PLUS\ and \<^verbatim>\C+MINUS\ for adjusting the editor font size depend on platform details and national keyboards. \<^bold>\Workaround:\ Rebind keys via \<^emph>\Global Options~/ Shortcuts\. \<^item> \<^bold>\Problem:\ The macOS key sequence \<^verbatim>\COMMAND+COMMA\ for application \<^emph>\Preferences\ is in conflict with the jEdit default keyboard shortcut for \<^emph>\Incremental Search Bar\ (action @{action_ref "quick-search"}). \<^bold>\Workaround:\ Rebind key via \<^emph>\Global Options~/ Shortcuts\ according to the national keyboard layout, e.g.\ \<^verbatim>\COMMAND+SLASH\ on English ones. \<^item> \<^bold>\Problem:\ On macOS with native Apple look-and-feel, some exotic national keyboards may cause a conflict of menu accelerator keys with regular jEdit key bindings. This leads to duplicate execution of the corresponding jEdit action. \<^bold>\Workaround:\ Disable the native Apple menu bar via Java runtime option \<^verbatim>\-Dapple.laf.useScreenMenuBar=false\. \<^item> \<^bold>\Problem:\ macOS system fonts sometimes lead to character drop-outs in the main text area. \<^bold>\Workaround:\ Use the default \<^verbatim>\Isabelle DejaVu\ fonts. \<^item> \<^bold>\Problem:\ On macOS the Java printer dialog sometimes does not work. \<^bold>\Workaround:\ Use action @{action isabelle.draft} and print via the Web browser. \<^item> \<^bold>\Problem:\ Antialiased text rendering may show bad performance or bad visual quality, notably on Linux/X11. \<^bold>\Workaround:\ The property \<^verbatim>\view.antiAlias\ (via menu item Utilities / Global Options / Text Area / Anti Aliased smooth text) has the main impact on text rendering, but some related properties may also change the behaviour. The default is \<^verbatim>\view.antiAlias=subpixel HRGB\: it can be much faster than \<^verbatim>\standard\, but occasionally causes problems with odd color shades. An alternative is to have \<^verbatim>\view.antiAlias=standard\ and set a Java system property like this:\<^footnote>\See also \<^url>\https://docs.oracle.com/javase/10/troubleshoot/java-2d-pipeline-rendering-and-properties.htm\.\ @{verbatim [display] \isabelle jedit -Dsun.java2d.opengl=true\} If this works reliably, it can be made persistent via @{setting JEDIT_JAVA_OPTIONS} within \<^path>\$ISABELLE_HOME_USER/etc/settings\. For the Isabelle desktop ``app'', there is a corresponding file with Java runtime options in the main directory (name depends on the OS platform). \<^item> \<^bold>\Problem:\ Some Linux/X11 input methods such as IBus tend to disrupt key event handling of Java/AWT/Swing. \<^bold>\Workaround:\ Do not use X11 input methods. Note that environment variable \<^verbatim>\XMODIFIERS\ is reset by default within Isabelle settings. \<^item> \<^bold>\Problem:\ Some Linux/X11 window managers that are not ``re-parenting'' cause problems with additional windows opened by Java. This affects either historic or neo-minimalistic window managers like \<^verbatim>\awesome\ or \<^verbatim>\xmonad\. \<^bold>\Workaround:\ Use a regular re-parenting X11 window manager. \<^item> \<^bold>\Problem:\ Various forks of Linux/X11 window managers and desktop environments (like Gnome) disrupt the handling of menu popups and mouse positions of Java/AWT/Swing. \<^bold>\Workaround:\ Use suitable version of Linux desktops. \<^item> \<^bold>\Problem:\ Full-screen mode via jEdit action @{action_ref "toggle-full-screen"} (default keyboard shortcut \<^verbatim>\F11\ or \<^verbatim>\S+F11\) works robustly on Windows, but not on macOS or various Linux/X11 window managers. For the latter platforms, it is approximated by educated guesses on the window size (excluding the macOS menu bar). \<^bold>\Workaround:\ Use native full-screen control of the macOS window manager. \<^item> \<^bold>\Problem:\ Heap space of the JVM may fill up and render the Prover IDE unresponsive, e.g.\ when editing big Isabelle sessions with many theories. \<^bold>\Workaround:\ Increase JVM heap parameters by editing platform-specific files (for ``properties'' or ``options'') that are associated with the main app bundle. \ end diff --git a/src/Doc/System/Environment.thy b/src/Doc/System/Environment.thy --- a/src/Doc/System/Environment.thy +++ b/src/Doc/System/Environment.thy @@ -1,502 +1,502 @@ (*:maxLineLen=78:*) theory Environment imports Base begin chapter \The Isabelle system environment\ text \ This manual describes Isabelle together with related tools as seen from a system oriented view. See also the \<^emph>\Isabelle/Isar Reference Manual\ @{cite "isabelle-isar-ref"} for the actual Isabelle input language and related concepts, and \<^emph>\The Isabelle/Isar Implementation Manual\ @{cite "isabelle-implementation"} for the main concepts of the underlying implementation in Isabelle/ML. \ section \Isabelle settings \label{sec:settings}\ text \ Isabelle executables may depend on the \<^emph>\Isabelle settings\ within the process environment. This is a statically scoped collection of environment variables, such as @{setting ISABELLE_HOME}, @{setting ML_SYSTEM}, @{setting ML_HOME}. These variables are \<^emph>\not\ intended to be set directly from the shell, but are provided by Isabelle \<^emph>\components\ their \<^emph>\settings files\ as explained below. \ subsection \Bootstrapping the environment \label{sec:boot}\ text \ Isabelle executables need to be run within a proper settings environment. This is bootstrapped as described below, on the first invocation of one of the outer wrapper scripts (such as @{executable_ref isabelle}). This happens only once for each process tree, i.e.\ the environment is passed to subprocesses according to regular Unix conventions. \<^enum> The special variable @{setting_def ISABELLE_HOME} is determined automatically from the location of the binary that has been run. You should not try to set @{setting ISABELLE_HOME} manually. Also note that the Isabelle executables either have to be run from their original location in the distribution directory, or via the executable objects created by the @{tool install} tool. Symbolic links are admissible, but a plain copy of the \<^dir>\$ISABELLE_HOME/bin\ files will not work! \<^enum> The file \<^file>\$ISABELLE_HOME/etc/settings\ is run as a @{executable_ref bash} shell script with the auto-export option for variables enabled. This file holds a rather long list of shell variable assignments, thus providing the site-wide default settings. The Isabelle distribution already contains a global settings file with sensible defaults for most variables. When installing the system, only a few of these may have to be adapted (probably @{setting ML_SYSTEM} etc.). \<^enum> The file \<^path>\$ISABELLE_HOME_USER/etc/settings\ (if it exists) is run in the same way as the site default settings. Note that the variable @{setting ISABELLE_HOME_USER} has already been set before --- usually to something like \<^verbatim>\$USER_HOME/.isabelle/Isabelle2021\. Thus individual users may override the site-wide defaults. Typically, a user settings file contains only a few lines, with some assignments that are actually changed. Never copy the central \<^file>\$ISABELLE_HOME/etc/settings\ file! Since settings files are regular GNU @{executable_def bash} scripts, one may use complex shell commands, such as \<^verbatim>\if\ or \<^verbatim>\case\ statements to set variables depending on the system architecture or other environment variables. Such advanced features should be added only with great care, though. In particular, external environment references should be kept at a minimum. \<^medskip> A few variables are somewhat special, e.g.\ @{setting_def ISABELLE_TOOL} is set automatically to the absolute path name of the @{executable isabelle} executables. \<^medskip> Note that the settings environment may be inspected with the @{tool getenv} tool. This might help to figure out the effect of complex settings scripts. \ subsection \Common variables\ text \ This is a reference of common Isabelle settings variables. Note that the list is somewhat open-ended. Third-party utilities or interfaces may add their own selection. Variables that are special in some sense are marked with \\<^sup>*\. \<^descr>[@{setting_def USER_HOME}\\<^sup>*\] Is the cross-platform user home directory. On Unix systems this is usually the same as @{setting HOME}, but on Windows it is the regular home directory of the user, not the one of within the Cygwin root file-system.\<^footnote>\Cygwin itself offers another choice whether its HOME should point to the \<^path>\/home\ directory tree or the Windows user home.\ \<^descr>[@{setting_def ISABELLE_HOME}\\<^sup>*\] is the location of the top-level Isabelle distribution directory. This is automatically determined from the Isabelle executable that has been invoked. Do not attempt to set @{setting ISABELLE_HOME} yourself from the shell! \<^descr>[@{setting_def ISABELLE_HOME_USER}] is the user-specific counterpart of @{setting ISABELLE_HOME}. The default value is relative to \<^path>\$USER_HOME/.isabelle\, under rare circumstances this may be changed in the global setting file. Typically, the @{setting ISABELLE_HOME_USER} directory mimics @{setting ISABELLE_HOME} to some extend. In particular, site-wide defaults may be overridden by a private \<^verbatim>\$ISABELLE_HOME_USER/etc/settings\. \<^descr>[@{setting_def ISABELLE_PLATFORM_FAMILY}\\<^sup>*\] is automatically set to the general platform family (\<^verbatim>\linux\, \<^verbatim>\macos\, \<^verbatim>\windows\). Note that platform-dependent tools usually need to refer to the more specific identification according to @{setting ISABELLE_PLATFORM64}, @{setting ISABELLE_WINDOWS_PLATFORM64}, @{setting ISABELLE_APPLE_PLATFORM64}. \<^descr>[@{setting_def ISABELLE_PLATFORM64}\\<^sup>*\] indicates the standard Posix platform (\<^verbatim>\x86_64\, \<^verbatim>\arm64\), together with a symbolic name for the operating system (\<^verbatim>\linux\, \<^verbatim>\darwin\, \<^verbatim>\cygwin\). \<^descr>[@{setting_def ISABELLE_WINDOWS_PLATFORM64}\\<^sup>*\, @{setting_def ISABELLE_WINDOWS_PLATFORM32}\\<^sup>*\] indicate the native Windows platform: both 64\,bit and 32\,bit executables are supported here. In GNU bash scripts, a preference for native Windows platform variants may be specified like this (first 64 bit, second 32 bit): @{verbatim [display] \"${ISABELLE_WINDOWS_PLATFORM64:-${ISABELLE_WINDOWS_PLATFORM32:- $ISABELLE_PLATFORM64}}"\} \<^descr>[@{setting_def ISABELLE_APPLE_PLATFORM64}\\<^sup>*\] indicates the native Apple Silicon platform (\<^verbatim>\arm64-darwin\ if available), instead of Intel emulation via Rosetta (\<^verbatim>\ISABELLE_PLATFORM64=x86_64-darwin\). \<^descr>[@{setting ISABELLE_TOOL}\\<^sup>*\] is automatically set to the full path name of the @{executable isabelle} executable. \<^descr>[@{setting_def ISABELLE_IDENTIFIER}\\<^sup>*\] refers to the name of this Isabelle distribution, e.g.\ ``\<^verbatim>\Isabelle2021\''. \<^descr>[@{setting_def ML_SYSTEM}, @{setting_def ML_HOME}, @{setting_def ML_OPTIONS}, @{setting_def ML_PLATFORM}, @{setting_def ML_IDENTIFIER}\\<^sup>*\] specify the underlying ML system to be used for Isabelle. There is only a fixed set of admissable @{setting ML_SYSTEM} names (see the \<^file>\$ISABELLE_HOME/etc/settings\ file of the distribution). The actual compiler binary will be run from the directory @{setting ML_HOME}, with @{setting ML_OPTIONS} as first arguments on the command line. The optional @{setting ML_PLATFORM} may specify the binary format of ML heap images, which is useful for cross-platform installations. The value of @{setting ML_IDENTIFIER} is automatically obtained by composing the values of @{setting ML_SYSTEM}, @{setting ML_PLATFORM} and the Isabelle version values. \<^descr>[@{setting_def ISABELLE_JDK_HOME}] points to a full JDK (Java Development Kit) installation with \<^verbatim>\javac\ and \<^verbatim>\jar\ executables. Note that conventional \<^verbatim>\JAVA_HOME\ points to the JRE (Java Runtime Environment), not the JDK. \<^descr>[@{setting_def ISABELLE_JAVA_PLATFORM}] identifies the hardware and operating system platform for the Java installation of Isabelle. That is always the (native) 64 bit variant: \<^verbatim>\x86_64-linux\, \<^verbatim>\x86_64-darwin\, \<^verbatim>\x86_64-windows\. \<^descr>[@{setting_def ISABELLE_BROWSER_INFO}] is the directory where HTML and PDF browser information is stored (see also \secref{sec:info}); its default is \<^path>\$ISABELLE_HOME_USER/browser_info\. For ``system build mode'' (see \secref{sec:tool-build}), @{setting_def ISABELLE_BROWSER_INFO_SYSTEM} is used instead; its default is \<^path>\$ISABELLE_HOME/browser_info\. \<^descr>[@{setting_def ISABELLE_HEAPS}] is the directory where session heap images, log files, and build databases are stored; its default is \<^path>\$ISABELLE_HOME_USER/heaps\. If @{system_option system_heaps} is \<^verbatim>\true\, @{setting_def ISABELLE_HEAPS_SYSTEM} is used instead; its default is \<^path>\$ISABELLE_HOME/heaps\. See also \secref{sec:tool-build}. \<^descr>[@{setting_def ISABELLE_LOGIC}] specifies the default logic to load if none is given explicitely by the user. The default value is \<^verbatim>\HOL\. \<^descr>[@{setting_def ISABELLE_LINE_EDITOR}] specifies the line editor for the @{tool_ref console} interface. \<^descr>[@{setting_def ISABELLE_PDFLATEX}, @{setting_def ISABELLE_LUALATEX}, @{setting_def ISABELLE_BIBTEX}, @{setting_def ISABELLE_MAKEINDEX}] refer to {\LaTeX}-related tools for Isabelle document preparation (see also \secref{sec:tool-document}). \<^descr>[@{setting_def ISABELLE_TOOLS}] is a colon separated list of directories that are scanned by @{executable isabelle} for external utility programs (see also \secref{sec:isabelle-tool}). \<^descr>[@{setting_def ISABELLE_DOCS}] is a colon separated list of directories with documentation files. \<^descr>[@{setting_def PDF_VIEWER}] specifies the program to be used for displaying \<^verbatim>\pdf\ files. \<^descr>[@{setting_def ISABELLE_TMP_PREFIX}\\<^sup>*\] is the prefix from which any running Isabelle ML process derives an individual directory for temporary files. \<^descr>[@{setting_def ISABELLE_TOOL_JAVA_OPTIONS}] is passed to the \<^verbatim>\java\ executable when running Isabelle tools (e.g.\ @{tool build}). This is occasionally helpful to provide more heap space, via additional options like \<^verbatim>\-Xms1g -Xmx4g\. \ subsection \Additional components \label{sec:components}\ text \ Any directory may be registered as an explicit \<^emph>\Isabelle component\. The general layout conventions are that of the main Isabelle distribution itself, and the following two files (both optional) have a special meaning: \<^item> \<^verbatim>\etc/settings\ holds additional settings that are initialized when bootstrapping the overall Isabelle environment, cf.\ \secref{sec:boot}. As usual, the content is interpreted as a GNU bash script. It may refer to the component's enclosing directory via the \<^verbatim>\COMPONENT\ shell variable. For example, the following setting allows to refer to files within the component later on, without having to hardwire absolute paths: @{verbatim [display] \MY_COMPONENT_HOME="$COMPONENT"\} Components can also add to existing Isabelle settings such as @{setting_def ISABELLE_TOOLS}, in order to provide component-specific tools that can be invoked by end-users. For example: @{verbatim [display] \ISABELLE_TOOLS="$ISABELLE_TOOLS:$COMPONENT/lib/Tools"\} \<^item> \<^verbatim>\etc/components\ holds a list of further sub-components of the same structure. The directory specifications given here can be either absolute (with leading \<^verbatim>\/\) or relative to the component's main directory. The root of component initialization is @{setting ISABELLE_HOME} itself. After initializing all of its sub-components recursively, @{setting ISABELLE_HOME_USER} is included in the same manner (if that directory exists). This allows to install private components via \<^path>\$ISABELLE_HOME_USER/etc/components\, although it is often more convenient to do that programmatically via the \<^bash_function>\init_component\ shell function in the \<^verbatim>\etc/settings\ script of \<^verbatim>\$ISABELLE_HOME_USER\ (or any other component directory). For example: @{verbatim [display] \init_component "$HOME/screwdriver-2.0"\} This is tolerant wrt.\ missing component directories, but might produce a warning. \<^medskip> More complex situations may be addressed by initializing components listed in a given catalog file, relatively to some base directory: @{verbatim [display] \init_components "$HOME/my_component_store" "some_catalog_file"\} The component directories listed in the catalog file are treated as relative to the given base directory. See also \secref{sec:tool-components} for some tool-support for resolving components that are formally initialized but not installed yet. \ section \The Isabelle tool wrapper \label{sec:isabelle-tool}\ text \ The main \<^emph>\Isabelle tool wrapper\ provides a generic startup environment for Isabelle-related utilities, user interfaces, add-on applications etc. Such tools automatically benefit from the settings mechanism (\secref{sec:settings}). Moreover, this is the standard way to invoke Isabelle/Scala functionality as a separate operating-system process. Isabelle command-line tools are run uniformly via a common wrapper --- @{executable_ref isabelle}: @{verbatim [display] \Usage: isabelle TOOL [ARGS ...] Start Isabelle TOOL with ARGS; pass "-?" for tool-specific help. Available tools: ...\} Tools may be implemented in Isabelle/Scala or as stand-alone executables (usually as GNU bash scripts). In the invocation of ``@{executable isabelle}~\tool\'', the named \tool\ is resolved as follows (and in the given order). \<^enum> An external tool found on the directories listed in the @{setting ISABELLE_TOOLS} settings variable (colon-separated list in standard POSIX notation). \<^enum> If a file ``\tool\\<^verbatim>\.scala\'' is found, the source needs to define some object that extends the class \<^verbatim>\Isabelle_Tool.Body\. The Scala compiler is invoked on the spot (which may take some time), and the body function is run with the command-line arguments as \<^verbatim>\List[String]\. \<^enum> If an executable file ``\tool\'' is found, it is invoked as stand-alone program with the command-line arguments provided as \<^verbatim>\argv\ array. \<^enum> An internal tool that is registered in \<^verbatim>\etc/settings\ via the shell function \<^bash_function>\isabelle_scala_service\, referring to a suitable instance of class \<^scala_type>\isabelle.Isabelle_Scala_Tools\. This is the preferred approach for non-trivial systems programming in Isabelle/Scala: instead of adhoc interpretation of \<^verbatim>\scala\ scripts, which is somewhat slow and only type-checked at runtime, there are properly compiled \<^verbatim>\jar\ modules (see also the shell function \<^bash_function>\classpath\ in \secref{sec:scala}). There are also various administrative tools that are available from a bare repository clone of Isabelle, but not in regular distributions. \ subsubsection \Examples\ text \ Show the list of available documentation of the Isabelle distribution: @{verbatim [display] \isabelle doc\} View a certain document as follows: @{verbatim [display] \isabelle doc system\} Query the Isabelle settings environment: @{verbatim [display] \isabelle getenv ISABELLE_HOME_USER\} \ section \The raw Isabelle ML process\ subsection \Batch mode \label{sec:tool-process}\ text \ The @{tool_def process} tool runs the raw ML process in batch mode: @{verbatim [display] \Usage: isabelle process [OPTIONS] Options are: -T THEORY load theory -d DIR include session directory -e ML_EXPR evaluate ML expression on startup -f ML_FILE evaluate ML file on startup -l NAME logic session name (default ISABELLE_LOGIC="HOL") -m MODE add print mode for output -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) Run the raw Isabelle ML process in batch mode.\} \<^medskip> Options \<^verbatim>\-e\ and \<^verbatim>\-f\ allow to evaluate ML code, before the ML process is started. The source is either given literally or taken from a file. Multiple \<^verbatim>\-e\ and \<^verbatim>\-f\ options are evaluated in the given order. Errors lead to premature exit of the ML process with return code 1. \<^medskip> Option \<^verbatim>\-T\ loads a specified theory file. This is a wrapper for \<^verbatim>\-e\ with a suitable \<^ML>\use_thy\ invocation. \<^medskip> Option \<^verbatim>\-l\ specifies the logic session name. Option \<^verbatim>\-d\ specifies additional directories for session roots, see also \secref{sec:tool-build}. \<^medskip> The \<^verbatim>\-m\ option adds identifiers of print modes to be made active for this session. For example, \<^verbatim>\-m ASCII\ prefers ASCII replacement syntax over mathematical Isabelle symbols. \<^medskip> Option \<^verbatim>\-o\ allows to override Isabelle system options for this process, see also \secref{sec:system-options}. \ subsubsection \Examples\ text \ The subsequent example retrieves the \<^verbatim>\Main\ theory value from the theory loader within ML: @{verbatim [display] \isabelle process -e 'Thy_Info.get_theory "Main"'\} Observe the delicate quoting rules for the GNU bash shell vs.\ ML. The Isabelle/ML and Scala libraries provide functions for that, but here we need to do it manually. \<^medskip> This is how to invoke a function body with proper return code and printing of errors, and without printing of a redundant \<^verbatim>\val it = (): unit\ result: @{verbatim [display] \isabelle process -e 'Command_Line.tool (fn () => writeln "OK")'\} @{verbatim [display] \isabelle process -e 'Command_Line.tool (fn () => error "Bad")'\} \ subsection \Interactive mode\ text \ The @{tool_def console} tool runs the raw ML process with interactive console and line editor: @{verbatim [display] \Usage: isabelle console [OPTIONS] Options are: -d DIR include session directory -i NAME include session in name-space of theories -l NAME logic session name (default ISABELLE_LOGIC) -m MODE add print mode for output -n no build of session image on startup -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) -r bootstrap from raw Poly/ML Build a logic session image and run the raw Isabelle ML process in interactive mode, with line editor ISABELLE_LINE_EDITOR.\} \<^medskip> Option \<^verbatim>\-l\ specifies the logic session name. By default, its heap image is checked and built on demand, but the option \<^verbatim>\-n\ skips that. Option \<^verbatim>\-i\ includes additional sessions into the name-space of theories: multiple occurrences are possible. Option \<^verbatim>\-r\ indicates a bootstrap from the raw Poly/ML system, which is relevant for Isabelle/Pure development. \<^medskip> Options \<^verbatim>\-d\, \<^verbatim>\-m\, \<^verbatim>\-o\ have the same meaning as for @{tool process} (\secref{sec:tool-process}). \<^medskip> The Isabelle/ML process is run through the line editor that is specified via the settings variable @{setting ISABELLE_LINE_EDITOR} (e.g.\ @{executable_def rlwrap} for GNU readline); the fall-back is to use plain standard input/output. The user is connected to the raw ML toplevel loop: this is neither Isabelle/Isar nor Isabelle/ML within the usual formal context. The most relevant ML commands at this stage are \<^ML>\use\ (for ML files) and \<^ML>\use_thy\ (for theory files). \ section \The raw Isabelle Java process \label{sec:isabelle-java}\ text \ The @{executable_ref isabelle_java} executable allows to run a Java process within the name space of Java and Scala components that are bundled with Isabelle, but \<^emph>\without\ the Isabelle settings environment (\secref{sec:settings}). After such a JVM cold-start, the Isabelle environment can be accessed via \<^verbatim>\Isabelle_System.getenv\ as usual, but the underlying process environment remains clean. This is e.g.\ relevant when invoking other processes that should remain separate from the current Isabelle installation. \<^medskip> Note that under normal circumstances, Isabelle command-line tools are run \<^emph>\within\ the settings environment, as provided by the @{executable isabelle} wrapper (\secref{sec:isabelle-tool} and \secref{sec:tool-java}). \ subsubsection \Example\ text \ The subsequent example creates a raw Java process on the command-line and invokes the main Isabelle application entry point: - @{verbatim [display] \isabelle_java isabelle.Main\} + @{verbatim [display] \isabelle_java isabelle.jedit.Main\} \ section \YXML versus XML \label{sec:yxml-vs-xml}\ text \ Isabelle tools often use YXML, which is a simple and efficient syntax for untyped XML trees. The YXML format is defined as follows. \<^enum> The encoding is always UTF-8. \<^enum> Body text is represented verbatim (no escaping, no special treatment of white space, no named entities, no CDATA chunks, no comments). \<^enum> Markup elements are represented via ASCII control characters \\<^bold>X = 5\ and \\<^bold>Y = 6\ as follows: \begin{tabular}{ll} XML & YXML \\\hline \<^verbatim>\<\\name attribute\\<^verbatim>\=\\value \\\<^verbatim>\>\ & \\<^bold>X\<^bold>Yname\<^bold>Yattribute\\<^verbatim>\=\\value\\<^bold>X\ \\ \<^verbatim>\\name\\<^verbatim>\>\ & \\<^bold>X\<^bold>Y\<^bold>X\ \\ \end{tabular} There is no special case for empty body text, i.e.\ \<^verbatim>\\ is treated like \<^verbatim>\\. Also note that \\<^bold>X\ and \\<^bold>Y\ may never occur in well-formed XML documents. Parsing YXML is pretty straight-forward: split the text into chunks separated by \\<^bold>X\, then split each chunk into sub-chunks separated by \\<^bold>Y\. Markup chunks start with an empty sub-chunk, and a second empty sub-chunk indicates close of an element. Any other non-empty chunk consists of plain text. For example, see \<^file>\~~/src/Pure/PIDE/yxml.ML\ or \<^file>\~~/src/Pure/PIDE/yxml.scala\. YXML documents may be detected quickly by checking that the first two characters are \\<^bold>X\<^bold>Y\. \ end diff --git a/src/Doc/System/Scala.thy b/src/Doc/System/Scala.thy --- a/src/Doc/System/Scala.thy +++ b/src/Doc/System/Scala.thy @@ -1,337 +1,337 @@ (*:maxLineLen=78:*) theory Scala imports Base begin chapter \Isabelle/Scala systems programming \label{sec:scala}\ text \ Isabelle/ML and Isabelle/Scala are the two main implementation languages of the Isabelle environment: \<^item> Isabelle/ML is for \<^emph>\mathematics\, to develop tools within the context of symbolic logic, e.g.\ for constructing proofs or defining domain-specific formal languages. See the \<^emph>\Isabelle/Isar implementation manual\ @{cite "isabelle-implementation"} for more details. \<^item> Isabelle/Scala is for \<^emph>\physics\, to connect with the world of systems and services, including editors and IDE frameworks. There are various ways to access Isabelle/Scala modules and operations: \<^item> Isabelle command-line tools (\secref{sec:scala-tools}) run in a separate Java process. \<^item> Isabelle/ML antiquotations access Isabelle/Scala functions (\secref{sec:scala-functions}) via the PIDE protocol: execution happens within the running Java process underlying Isabelle/Scala. \<^item> The \<^verbatim>\Console/Scala\ plugin of Isabelle/jEdit @{cite "isabelle-jedit"} operates on the running Java application, using the Scala read-eval-print-loop (REPL). - The main Isabelle/Scala functionality is provided by \<^verbatim>\Pure.jar\, but + The main Isabelle/Scala functionality is provided by \<^verbatim>\isabelle.jar\, but further add-ons are bundled with Isabelle, e.g.\ to access SQLite or PostgreSQL using JDBC (Java Database Connectivity). Other components may augment the system environment by providing a suitable \<^path>\etc/settings\ shell script in the component directory. Some shell functions are available to help with that: \<^item> Function \<^bash_function>\classpath\ adds \<^verbatim>\jar\ files in Isabelle path notation (POSIX). On Windows, this is converted to native path names before invoking @{tool java} or @{tool scala} (\secref{sec:scala-tools}). \<^item> Function \<^bash_function>\isabelle_scala_service\ registers global service providers as subclasses of \<^scala_type>\isabelle.Isabelle_System.Service\, using the raw Java name according to @{scala_method (in java.lang.Object) getClass} (it should be enclosed in single quotes to avoid special characters like \<^verbatim>\$\ to be interpreted by the shell). Particular Isabelle/Scala services require particular subclasses: instances are filtered according to their dynamic type. For example, class \<^scala_type>\isabelle.Isabelle_Scala_Tools\ collects Scala command-line tools, and class \<^scala_type>\isabelle.Scala.Functions\ collects Scala functions (\secref{sec:scala-functions}). \ section \Command-line tools \label{sec:scala-tools}\ subsection \Java Runtime Environment \label{sec:tool-java}\ text \ The @{tool_def java} tool is a direct wrapper for the Java Runtime Environment, within the regular Isabelle settings environment (\secref{sec:settings}) and Isabelle classpath. The command line arguments are that of the bundled Java distribution: see option \<^verbatim>\-help\ in particular. The \<^verbatim>\java\ executable is taken from @{setting ISABELLE_JDK_HOME}, according to the standard directory layout for regular distributions of OpenJDK. The shell function \<^bash_function>\isabelle_jdk\ allows shell scripts to invoke other Java tools robustly (e.g.\ \<^verbatim>\isabelle_jdk jar\), without depending on accidental operating system installations. \ subsection \Scala toplevel \label{sec:tool-scala}\ text \ The @{tool_def scala} tool is a direct wrapper for the Scala toplevel, similar to @{tool java} above. The command line arguments are that of the bundled Scala distribution: see option \<^verbatim>\-help\ in particular. This allows to interact with Isabelle/Scala interactively. \ subsubsection \Example\ text \ Explore the Isabelle system environment in Scala: @{verbatim [display, indent = 2] \$ isabelle scala\} @{scala [display, indent = 2] \import isabelle._ val isabelle_home = Isabelle_System.getenv("ISABELLE_HOME") val options = Options.init() options.bool("browser_info") options.string("document")\} \ subsection \Scala compiler \label{sec:tool-scalac}\ text \ The @{tool_def scalac} tool is a direct wrapper for the Scala compiler; see also @{tool scala} above. The command line arguments are that of the bundled Scala distribution. This allows to compile further Scala modules, depending on existing Isabelle/Scala functionality. The resulting \<^verbatim>\class\ or \<^verbatim>\jar\ files can be added to the Java classpath using the shell function \<^bash_function>\classpath\. Thus add-on components can register themselves in a modular manner, see also \secref{sec:components}. Note that Isabelle/jEdit @{cite "isabelle-jedit"} has its own mechanisms for adding plugin components. This needs special attention, since it overrides the standard Java class loader. \ subsection \Scala script wrapper\ text \ The executable @{executable "$ISABELLE_HOME/bin/isabelle_scala_script"} allows to run Isabelle/Scala source files stand-alone programs, by using a suitable ``hash-bang'' line and executable file permissions. For example: @{verbatim [display, indent = 2] \#!/usr/bin/env isabelle_scala_script\} @{scala [display, indent = 2] \val options = isabelle.Options.init() Console.println("browser_info = " + options.bool("browser_info")) Console.println("document = " + options.string("document"))\} This assumes that the executable may be found via the @{setting PATH} from the process environment: this is the case when Isabelle settings are active, e.g.\ in the context of the main Isabelle tool wrapper \secref{sec:isabelle-tool}. Alternatively, the full \<^file>\$ISABELLE_HOME/bin/isabelle_scala_script\ may be specified in expanded form. \ subsection \Project setup for common Scala IDEs\ text \ The @{tool_def scala_project} tool creates a project configuration for Isabelle/Scala/jEdit: @{verbatim [display] \Usage: isabelle scala_project [OPTIONS] PROJECT_DIR Options are: -L make symlinks to original scala files Setup Gradle project for Isabelle/Scala/jEdit --- to support Scala IDEs such as IntelliJ IDEA.\} The generated configuration is for Gradle\<^footnote>\\<^url>\https://gradle.org\\, but the main purpose is to import it into common Scala IDEs, such as IntelliJ IDEA\<^footnote>\\<^url>\https://www.jetbrains.com/idea\\. This allows to explore the sources with static analysis and other hints in real-time. The specified project directory needs to be fresh. The generated files refer to physical file-system locations, using the path notation of the underlying OS platform. Thus the project needs to be recreated whenever the Isabelle installation is changed or moved. \<^medskip> By default, Scala sources are \<^emph>\copied\ from the Isabelle distribution and editing them within the IDE has no permanent effect. Option \<^verbatim>\-L\ produces \<^emph>\symlinks\ to the original files: this allows to develop Isabelle/Scala/jEdit within an external Scala IDE. Note that building the result always requires \<^verbatim>\isabelle jedit -b\ on the command-line. \ section \Registered Isabelle/Scala functions \label{sec:scala-functions}\ subsection \Defining functions in Isabelle/Scala\ text \ A Scala functions of type \<^scala_type>\String => String\ may be wrapped as \<^scala_type>\isabelle.Scala.Fun\ and collected via an instance of the class \<^scala_type>\isabelle.Scala.Functions\. A system component can then register that class via \<^bash_function>\isabelle_scala_service\ in \<^path>\etc/settings\ (\secref{sec:components}). An example is the predefined collection of \<^scala_type>\isabelle.Scala.Functions\ in - Isabelle/\<^verbatim>\Pure.jar\ with the following line in + \<^verbatim>\isabelle.jar\ with the following line in \<^file>\$ISABELLE_HOME/etc/settings\: @{verbatim [display, indent = 2] \isabelle_scala_service 'isabelle.Functions'\} The overall list of registered functions is accessible in Isabelle/Scala as \<^scala_object>\isabelle.Scala.functions\. \ subsection \Invoking functions in Isabelle/ML\ text \ Isabelle/PIDE provides a protocol to invoke registered Scala functions in ML: this works both within the Prover IDE and in batch builds. The subsequent ML antiquotations refer to Scala functions in a formally-checked manner. \begin{matharray}{rcl} @{ML_antiquotation_def "scala_function"} & : & \ML_antiquotation\ \\ @{ML_antiquotation_def "scala"} & : & \ML_antiquotation\ \\ \end{matharray} \<^rail>\ (@{ML_antiquotation scala_function} | @{ML_antiquotation scala}) @{syntax embedded} \ \<^descr> \@{scala_function name}\ inlines the checked function name as ML string literal. \<^descr> \@{scala name}\ and \@{scala_thread name}\ invoke the checked function via the PIDE protocol. In Isabelle/ML this appears as a function of type \<^ML_type>\string -> string\, which is subject to interrupts within the ML runtime environment as usual. A \<^scala>\null\ result in Scala raises an exception \<^ML>\Scala.Null\ in ML. The execution of \@{scala}\ works via a Scala future on a bounded thread farm, while \@{scala_thread}\ always forks a separate Java thread. The standard approach of representing datatypes via strings works via XML in YXML transfer syntax. See Isabelle/ML operations and modules @{ML YXML.string_of_body}, @{ML YXML.parse_body}, @{ML_structure XML.Encode}, @{ML_structure XML.Decode}; similarly for Isabelle/Scala. Isabelle symbols may have to be recoded via Scala operations \<^scala_method>\isabelle.Symbol.decode\ and \<^scala_method>\isabelle.Symbol.encode\. \ subsubsection \Examples\ text \ Invoke the predefined Scala function \<^scala_function>\echo\: \ ML \ val s = "test"; val s' = \<^scala>\echo\ s; \<^assert> (s = s') \ text \ Let the Scala compiler process some toplevel declarations, producing a list of errors: \ ML \ val source = "class A(a: Int, b: Boolean)" val errors = \<^scala>\scala_toplevel\ source |> YXML.parse_body |> let open XML.Decode in list string end; \<^assert> (null errors)\ text \ The above is merely for demonstration. See \<^ML>\Scala_Compiler.toplevel\ for a more convenient version with builtin decoding and treatment of errors. \ section \Documenting Isabelle/Scala entities\ text \ The subsequent document antiquotations help to document Isabelle/Scala entities, with formal checking of names against the Isabelle classpath. \begin{matharray}{rcl} @{antiquotation_def "scala"} & : & \antiquotation\ \\ @{antiquotation_def "scala_object"} & : & \antiquotation\ \\ @{antiquotation_def "scala_type"} & : & \antiquotation\ \\ @{antiquotation_def "scala_method"} & : & \antiquotation\ \\ \end{matharray} \<^rail>\ (@@{antiquotation scala} | @@{antiquotation scala_object}) @{syntax embedded} ; @@{antiquotation scala_type} @{syntax embedded} types ; @@{antiquotation scala_method} class @{syntax embedded} types args ; class: ('(' @'in' @{syntax name} types ')')? ; types: ('[' (@{syntax name} ',' +) ']')? ; args: ('(' (nat | (('_' | @{syntax name}) + ',')) ')')? \ \<^descr> \@{scala s}\ is similar to \@{verbatim s}\, but the given source text is checked by the Scala compiler as toplevel declaration (without evaluation). This allows to write Isabelle/Scala examples that are statically checked. \<^descr> \@{scala_object x}\ checks the given Scala object name (simple value or ground module) and prints the result verbatim. \<^descr> \@{scala_type T[A]}\ checks the given Scala type name (with optional type parameters) and prints the result verbatim. \<^descr> \@{scala_method (in c[A]) m[B](n)}\ checks the given Scala method \m\ in the context of class \c\. The method argument slots are either specified by a number \n\ or by a list of (optional) argument types; this may refer to type variables specified for the class or method: \A\ or \B\ above. Everything except for the method name \m\ is optional. The absence of the class context means that this is a static method. The absence of arguments with types means that the method can be determined uniquely as \<^verbatim>\(\\m\\<^verbatim>\ _)\ in Scala (no overloading). \ subsubsection \Examples\ text \ Miscellaneous Isabelle/Scala entities: \<^item> object: \<^scala_object>\isabelle.Isabelle_Process\ \<^item> type without parameter: @{scala_type isabelle.Console_Progress} \<^item> type with parameter: @{scala_type List[A]} \<^item> static method: \<^scala_method>\isabelle.Isabelle_System.bash\ \<^item> class and method with type parameters: @{scala_method (in List[A]) map[B]("A => B")} \<^item> overloaded method with argument type: @{scala_method (in Int) "+" (Int)} \ end diff --git a/src/HOL/SPARK/etc/settings b/src/HOL/SPARK/etc/settings deleted file mode 100644 --- a/src/HOL/SPARK/etc/settings +++ /dev/null @@ -1,4 +0,0 @@ -# -*- shell-script -*- :mode=shellscript: - -isabelle_scala_service 'isabelle.spark.SPARK$Load_Command1' -isabelle_scala_service 'isabelle.spark.SPARK$Load_Command2' diff --git a/src/HOL/Tools/etc/settings b/src/HOL/Tools/etc/settings --- a/src/HOL/Tools/etc/settings +++ b/src/HOL/Tools/etc/settings @@ -1,6 +1,3 @@ # -*- shell-script -*- :mode=shellscript: -isabelle_scala_service 'isabelle.nitpick.Kodkod$Handler' -isabelle_scala_service 'isabelle.nitpick.Scala_Functions' - ISABELLE_ATP="$COMPONENT/ATP" diff --git a/src/Pure/Admin/build_jedit.scala b/src/Pure/Admin/build_jedit.scala --- a/src/Pure/Admin/build_jedit.scala +++ b/src/Pure/Admin/build_jedit.scala @@ -1,530 +1,537 @@ /* Title: Pure/Admin/build_jedit.scala Author: Makarius -Build auxiliary jEdit component. +Build component for jEdit text-editor. */ package isabelle import java.nio.charset.Charset import scala.jdk.CollectionConverters._ object Build_JEdit { /* modes */ object Mode { val empty: Mode = new Mode("", "", Nil) val init: Mode = empty + ("noWordSep" -> """_'?⇩\^<>""") + ("unalignedOpenBrackets" -> "{[(«‹⟨⌈⌊⦇⟦⦃⦉") + ("unalignedCloseBrackets" -> "⦊⦄⟧⦈⌋⌉⟩›»)]}") + ("tabSize" -> "2") + ("indentSize" -> "2") val list: List[Mode] = { val isabelle_news: Mode = init.define("isabelle-news", "Isabelle NEWS") val isabelle: Mode = init.define("isabelle", "Isabelle theory") + ("commentStart" -> "(*") + ("commentEnd" -> "*)") val isabelle_ml: Mode = isabelle.define("isabelle-ml", "Isabelle/ML") val isabelle_root: Mode = isabelle.define("isabelle-root", "Isabelle session root") val isabelle_options: Mode = isabelle.define("isabelle-options", "Isabelle options") val sml: Mode = init.define("sml", "Standard ML") + ("commentStart" -> "(*") + ("commentEnd" -> "*)") + ("noWordSep" -> "_'") List(isabelle_news, isabelle, isabelle_ml, isabelle_root, isabelle_options, sml) } } final case class Mode private(name: String, description: String, rev_props: Properties.T) { override def toString: String = name def define(a: String, b: String): Mode = new Mode(a, b, rev_props) def + (entry: Properties.Entry): Mode = new Mode(name, description, Properties.put(rev_props, entry)) def write(dir: Path): Unit = { require(name.nonEmpty && description.nonEmpty, "Bad Isabelle/jEdit mode content") val properties = rev_props.reverse.map(p => Symbol.spaces(4) + XML.string_of_tree(XML.elem(Markup("PROPERTY", List("NAME" -> p._1, "VALUE" -> p._2))))) File.write(dir + Path.basic(name).xml, """ """ + properties.mkString("\n", "\n", "") + """ """) } } /* build jEdit component */ private val download_jars: List[(String, String)] = List( "https://repo1.maven.org/maven2/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" -> "jsr305-3.0.2.jar") private val download_plugins: List[(String, String)] = List( "Code2HTML" -> "0.7", "CommonControls" -> "1.7.4", "Console" -> "5.1.4", "ErrorList" -> "2.4.0", "Highlight" -> "2.2", "Navigator" -> "2.7", "SideKick" -> "1.8") def build_jedit( component_dir: Path, version: String, original: Boolean = false, java_home: Path = default_java_home, progress: Progress = new Progress): Unit = { Isabelle_System.require_command("ant", test = "-version") Isabelle_System.require_command("patch") Isabelle_System.require_command("unzip", test = "-h") Isabelle_System.new_directory(component_dir) /* jEdit directory */ val jedit = "jedit" + version val jedit_patched = jedit + "-patched" val jedit_dir = Isabelle_System.make_directory(component_dir + Path.basic(jedit)) val jedit_patched_dir = component_dir + Path.basic(jedit_patched) def download_jedit(dir: Path, name: String, target_name: String = ""): Path = { val jedit_name = jedit + name val url = "https://sourceforge.net/projects/jedit/files/jedit/" + version + "/" + jedit_name + "/download" val path = dir + Path.basic(proper_string(target_name) getOrElse jedit_name) Isabelle_System.download_file(url, path, progress = progress) path } Isabelle_System.with_tmp_dir("tmp")(tmp_dir => { /* original version */ val install_path = download_jedit(tmp_dir, "install.jar") Isabelle_System.bash("""export CLASSPATH="" isabelle_java java -Duser.home=""" + File.bash_platform_path(tmp_dir) + " -jar " + File.bash_platform_path(install_path) + " auto " + File.bash_platform_path(jedit_dir) + " unix-script=off unix-man=off").check val source_path = download_jedit(tmp_dir, "source.tar.bz2") Isabelle_System.gnutar("-xjf " + File.bash_path(source_path), dir = jedit_dir).check /* patched version */ Isabelle_System.copy_dir(jedit_dir, jedit_patched_dir) val source_dir = jedit_patched_dir + Path.basic("jEdit") val tmp_source_dir = tmp_dir + Path.basic("jEdit") progress.echo("Patching jEdit sources ...") for { file <- File.find_files(Path.explode("~~/src/Tools/jEdit/patches").file).iterator name = file.getName if !name.endsWith("~") && !name.endsWith(".orig") } { progress.bash("patch -p2 < " + File.bash_path(File.path(file)), cwd = source_dir.file, echo = true).check } for { theme <- List("classic", "tango") } { val path = Path.explode("org/gjt/sp/jedit/icons/themes/" + theme + "/32x32/apps/isabelle.gif") Isabelle_System.copy_file(Path.explode("~~/lib/logo/isabelle_transparent-32.gif"), source_dir + path) } progress.echo("Building jEdit ...") Isabelle_System.copy_dir(source_dir, tmp_source_dir) progress.bash("env JAVA_HOME=" + File.bash_platform_path(java_home) + " ant", cwd = tmp_source_dir.file, echo = true).check Isabelle_System.copy_file(tmp_source_dir + Path.explode("build/jedit.jar"), jedit_patched_dir) }) /* jars */ val jars_dir = Isabelle_System.make_directory(jedit_patched_dir + Path.basic("jars")) for { (url, name) <- download_jars } { Isabelle_System.download_file(url, jars_dir + Path.basic(name), progress = progress) } for { (name, vers) <- download_plugins } { Isabelle_System.with_tmp_file("tmp", ext = "zip")(zip_path => { val url = "https://sourceforge.net/projects/jedit-plugins/files/" + name + "/" + vers + "/" + name + "-" + vers + "-bin.zip/download" Isabelle_System.download_file(url, zip_path, progress = progress) Isabelle_System.bash("unzip -x " + File.bash_path(zip_path), cwd = jars_dir.file).check }) } /* resources */ val keep_encodings = List("ISO-8859-1", "ISO-8859-15", "US-ASCII", "UTF-8", "windows-1252") val drop_encodings = Charset.availableCharsets().keySet().asScala.toList.sorted.filterNot(keep_encodings.contains) File.write(jedit_patched_dir + Path.explode("properties/jEdit.props"), """#jEdit properties autoReloadDialog=false buffer.deepIndent=false buffer.encoding=UTF-8-Isabelle buffer.indentSize=2 buffer.lineSeparator=\n buffer.maxLineLen=100 buffer.noTabs=true buffer.sidekick.keystroke-parse=false buffer.tabSize=2 buffer.undoCount=1000 close-docking-area.shortcut2=C+e C+CIRCUMFLEX complete-word.shortcut= console.dock-position=floating console.encoding=UTF-8 console.font=Isabelle DejaVu Sans Mono console.fontsize=14 delete-line.shortcut=A+d delete.shortcut2=C+d """ + drop_encodings.map(a => "encoding.opt-out." + a + "=true").mkString("\n") + """ encodingDetectors=BOM XML-PI buffer-local-property end.shortcut= expand-abbrev.shortcut2=CA+SPACE expand-folds.shortcut= fallbackEncodings=UTF-8 ISO-8859-15 US-ASCII firstTime=false focus-buffer-switcher.shortcut2=A+CIRCUMFLEX foldPainter=Circle gatchan.highlight.overview=false helpviewer.font=Isabelle DejaVu Serif helpviewer.fontsize=12 home.shortcut= hypersearch-results.dock-position=right insert-newline-indent.shortcut= insert-newline.shortcut= isabelle-debugger.dock-position=floating isabelle-documentation.dock-position=left isabelle-export-browser.label=Browse theory exports isabelle-output.dock-position=bottom isabelle-output.height=174 isabelle-output.width=412 isabelle-query.dock-position=bottom isabelle-session-browser.label=Browse session information isabelle-simplifier-trace.dock-position=floating isabelle-sledgehammer.dock-position=bottom isabelle-state.dock-position=right isabelle-symbols.dock-position=bottom isabelle-theories.dock-position=right isabelle.antiquoted_cartouche.label=Make antiquoted cartouche isabelle.complete-word.label=Complete word isabelle.complete.label=Complete Isabelle text isabelle.complete.shortcut2=C+b isabelle.control-bold.label=Control bold isabelle.control-bold.shortcut=C+e RIGHT isabelle.control-emph.label=Control emphasized isabelle.control-emph.shortcut=C+e LEFT isabelle.control-reset.label=Control reset isabelle.control-reset.shortcut=C+e BACK_SPACE isabelle.control-sub.label=Control subscript isabelle.control-sub.shortcut=C+e DOWN isabelle.control-sup.label=Control superscript isabelle.control-sup.shortcut=C+e UP isabelle.decrease-font-size.label=Decrease font size isabelle.decrease-font-size.shortcut2=C+SUBTRACT isabelle.decrease-font-size.shortcut=C+MINUS isabelle.decrease-font-size2.label=Decrease font size (clone) isabelle.draft.label=Show draft in browser isabelle.exclude-word-permanently.label=Exclude word permanently isabelle.exclude-word.label=Exclude word isabelle.first-error.label=Go to first error isabelle.first-error.shortcut=CS+a isabelle.goto-entity.label=Go to definition of formal entity at caret isabelle.goto-entity.shortcut=CS+d isabelle.include-word-permanently.label=Include word permanently isabelle.include-word.label=Include word isabelle.increase-font-size.label=Increase font size isabelle.increase-font-size.shortcut2=C+ADD isabelle.increase-font-size.shortcut=C+PLUS isabelle.increase-font-size2.label=Increase font size (clone) isabelle.increase-font-size2.shortcut=C+EQUALS isabelle.java-monitor.label=Java/VM monitor isabelle.last-error.label=Go to last error isabelle.last-error.shortcut=CS+z isabelle.message.label=Show message isabelle.message.shortcut=CS+m isabelle.newline.label=Newline with indentation of Isabelle keywords isabelle.newline.shortcut=ENTER isabelle.next-error.label=Go to next error isabelle.next-error.shortcut=CS+n isabelle.options.label=Isabelle options isabelle.prev-error.label=Go to previous error isabelle.prev-error.shortcut=CS+p isabelle.preview.label=Show preview in browser isabelle.reset-continuous-checking.label=Reset continuous checking isabelle.reset-font-size.label=Reset font size isabelle.reset-node-required.label=Reset node required isabelle.reset-words.label=Reset non-permanent words isabelle.select-entity.label=Select all occurences of formal entity at caret isabelle.select-entity.shortcut=CS+ENTER isabelle.set-continuous-checking.label=Set continuous checking isabelle.set-node-required.label=Set node required isabelle.toggle-breakpoint.label=Toggle Breakpoint isabelle.toggle-continuous-checking.label=Toggle continuous checking isabelle.toggle-continuous-checking.shortcut=C+e ENTER isabelle.toggle-node-required.label=Toggle node required isabelle.toggle-node-required.shortcut=C+e SPACE isabelle.tooltip.label=Show tooltip isabelle.tooltip.shortcut=CS+b isabelle.update-state.label=Update state output isabelle.update-state.shortcut=S+ENTER lang.usedefaultlocale=false largefilemode=full line-end.shortcut=END line-home.shortcut=HOME logo.icon.medium=32x32/apps/isabelle.gif lookAndFeel=com.formdev.flatlaf.FlatLightLaf match-bracket.shortcut2=C+9 metal.primary.font=Isabelle DejaVu Sans metal.primary.fontsize=12 metal.secondary.font=Isabelle DejaVu Sans metal.secondary.fontsize=12 navigator.showOnToolbar=true new-file-in-mode.shortcut= next-bracket.shortcut2=C+e C+9 options.shortcuts.deletekeymap.label=Delete options.shortcuts.duplicatekeymap.dialog.title=Keymap name options.shortcuts.duplicatekeymap.label=Duplicate options.shortcuts.resetkeymap.dialog.title=Reset keymap options.shortcuts.resetkeymap.label=Reset options.textarea.lineSpacing=1 plugin-blacklist.MacOSX.jar=true plugin.MacOSXPlugin.altDispatcher=false plugin.MacOSXPlugin.disableOption=true prev-bracket.shortcut2=C+e C+8 print.font=Isabelle DejaVu Sans Mono print.glyphVector=true recent-buffer.shortcut2=C+CIRCUMFLEX restore.remote=false restore=false search.subdirs.toggle=true select-block.shortcut2=C+8 sidekick-tree.dock-position=right sidekick.auto-complete-popup-get-focus=true sidekick.buffer-save-parse=true sidekick.complete-delay=0 sidekick.complete-instant.toggle=false sidekick.complete-popup.accept-characters=\\t sidekick.complete-popup.insert-characters= sidekick.persistentFilter=true sidekick.showFilter=true sidekick.splitter.location=721 systrayicon=false tip.show=false toggle-full-screen.shortcut2=S+F11 toggle-multi-select.shortcut2=C+NUMBER_SIGN toggle-rect-select.shortcut2=A+NUMBER_SIGN twoStageSave=false vfs.browser.dock-position=left vfs.favorite.0.type=1 vfs.favorite.0=$ISABELLE_HOME vfs.favorite.1.type=1 vfs.favorite.1=$ISABELLE_HOME_USER vfs.favorite.2.type=1 vfs.favorite.2=$JEDIT_HOME vfs.favorite.3.type=1 vfs.favorite.3=$JEDIT_SETTINGS vfs.favorite.4.type=1 vfs.favorite.4=isabelle-export: vfs.favorite.5.type=1 vfs.favorite.5=isabelle-session: view.antiAlias=subpixel HRGB view.blockCaret=true view.caretBlink=false view.docking.framework=PIDE view.eolMarkers=false view.extendedState=0 view.font=Isabelle DejaVu Sans Mono view.fontsize=18 view.fracFontMetrics=false view.gutter.font=Isabelle DejaVu Sans Mono view.gutter.fontsize=12 view.gutter.lineNumbers=false view.gutter.selectionAreaWidth=18 view.height=850 view.middleMousePaste=true view.showToolbar=true view.status.memory.background=#666699 view.status=( mode , fold , encoding ) locked wrap multiSelect rectSelect overwrite lineSep buffersets task-monitor java-status ml-status errors clock view.thickCaret=true view.width=1200 xml-insert-closing-tag.shortcut= """) val modes_dir = jedit_patched_dir + Path.basic("modes") Mode.list.foreach(_.write(modes_dir)) File.change_lines(modes_dir + Path.basic("catalog"), _.flatMap(line => if (line.containsSlice("FILE=\"ml.xml\"") || line.containsSlice("FILE_NAME_GLOB=\"*.{sml,ml}\"") || line.containsSlice("FILE_NAME_GLOB=\"*.ftl\"")) Nil else if (line.containsSlice("NAME=\"jamon\"")) { List( """""", "", """""", "", """""", "", """""", "", """""", "", line) } else if (line.containsSlice("NAME=\"sqr\"")) { List( """""", "", line) } else List(line))) /* doc */ val doc_dir = jedit_patched_dir + Path.basic("doc") download_jedit(doc_dir, "manual-a4.pdf", target_name = "jedit-manual.pdf") Isabelle_System.copy_file( doc_dir + Path.basic("CHANGES.txt"), doc_dir + Path.basic("jedit-changes")) File.write(doc_dir + Path.basic("Contents"), """Original jEdit Documentation jedit-manual jEdit User's Guide jedit-changes jEdit Version History """) /* diff */ Isabelle_System.bash( "diff -ru " + Bash.string(jedit) + " " + Bash.string(jedit_patched) + " > " + Bash.string(jedit + ".patch"), cwd = component_dir.file).check_rc(_ <= 1) if (!original) Isabelle_System.rm_tree(jedit_dir) /* etc */ val etc_dir = Isabelle_System.make_directory(component_dir + Path.explode("etc")) File.write(etc_dir + Path.explode("settings"), """# -*- shell-script -*- :mode=shellscript: -ISABELLE_JEDIT_HOME="$COMPONENT/""" + jedit_patched + """" -ISABELLE_JEDIT_JARS=""" + - File.read_dir(jars_dir).map("$ISABELLE_JEDIT_HOME/jars/" + _).mkString("\"", ":", "\"\n") -) +JEDIT_HOME="$COMPONENT/""" + jedit_patched + """" +JEDIT_JARS=""" + quote(File.read_dir(jars_dir).map("$JEDIT_HOME/jars/" + _).mkString(":")) + """ +JEDIT_JAR="$JEDIT_HOME/jedit.jar" +classpath "$JEDIT_JAR" + +JEDIT_SETTINGS="$ISABELLE_HOME_USER/jedit" +JEDIT_OPTIONS="-reuseview -nobackground -nosplash -log=9" +JEDIT_JAVA_OPTIONS="-Xms512m -Xmx4g -Xss16m" +JEDIT_JAVA_SYSTEM_OPTIONS="-Duser.language=en -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dapple.laf.useScreenMenuBar=true -Dapple.awt.application.name=Isabelle" + +ISABELLE_DOCS="$ISABELLE_DOCS:$JEDIT_HOME/doc" +""") /* README */ File.write(component_dir + Path.basic("README"), """This is a slightly patched version of jEdit """ + version + """ from https://sourceforge.net/projects/jedit/files/jedit with some additional plugins jars from https://sourceforge.net/projects/jedit-plugins/files Makarius """ + Date.Format.date(Date.now()) + "\n") } /** Isabelle tool wrappers **/ val default_version = "5.6.0" def default_java_home: Path = Path.explode("$JAVA_HOME").expand val isabelle_tool = - Isabelle_Tool("build_jedit", "build auxiliary jEdit component", Scala_Project.here, args => + Isabelle_Tool("build_jedit", "build Isabelle component from the jEdit text-editor", + Scala_Project.here, args => { var target_dir = Path.current var java_home = default_java_home var original = false var version = default_version val getopts = Getopts(""" Usage: isabelle build_jedit [OPTIONS] Options are: -D DIR target directory (default ".") -J JAVA_HOME Java version for building jedit.jar (e.g. version 11) -O retain copy of original jEdit directory -V VERSION jEdit version (default: """ + quote(default_version) + """) Build auxiliary jEdit component from original sources, with some patches. """, "D:" -> (arg => target_dir = Path.explode(arg)), "J:" -> (arg => java_home = Path.explode(arg)), "O" -> (_ => original = true), "V:" -> (arg => version = arg)) val more_args = getopts(args) if (more_args.nonEmpty) getopts.usage() - val component_dir = - target_dir + Path.basic("jedit_build-" + Date.Format.alt_date(Date.now())) - + val component_dir = target_dir + Path.basic("jedit-" + Date.Format.alt_date(Date.now())) val progress = new Console_Progress() build_jedit(component_dir, version, original = original, java_home = java_home, progress = progress) }) } diff --git a/src/Pure/Admin/build_release.scala b/src/Pure/Admin/build_release.scala --- a/src/Pure/Admin/build_release.scala +++ b/src/Pure/Admin/build_release.scala @@ -1,937 +1,935 @@ /* Title: Pure/Admin/build_release.scala Author: Makarius Build full Isabelle distribution from repository. */ package isabelle object Build_Release { /** release context **/ private def execute(dir: Path, script: String): Unit = Isabelle_System.bash(script, cwd = dir.file).check private def execute_tar(dir: Path, args: String, strip: Int = 0): Unit = Isabelle_System.gnutar(args, dir = dir, strip = strip).check object Release_Context { def apply( target_dir: Path, release_name: String = "", components_base: Path = Components.default_components_base, progress: Progress = new Progress): Release_Context = { val date = Date.now() val dist_name = proper_string(release_name) getOrElse ("Isabelle_" + Date.Format.date(date)) val dist_dir = (target_dir + Path.explode("dist-" + dist_name)).absolute new Release_Context(release_name, dist_name, dist_dir, components_base, progress) } } class Release_Context private[Build_Release]( val release_name: String, val dist_name: String, val dist_dir: Path, val components_base: Path, val progress: Progress) { override def toString: String = dist_name val isabelle: Path = Path.explode(dist_name) val isabelle_dir: Path = dist_dir + isabelle val isabelle_archive: Path = dist_dir + isabelle.tar.gz val isabelle_library_archive: Path = dist_dir + Path.explode(dist_name + "_library.tar.gz") def other_isabelle(dir: Path): Other_Isabelle = Other_Isabelle(dir + isabelle, isabelle_identifier = dist_name + "-build", progress = progress) def make_announce(id: String): Unit = { if (release_name.isEmpty) { File.write(isabelle_dir + Path.explode("ANNOUNCE"), """ IMPORTANT NOTE ============== This is a snapshot of Isabelle/""" + id + """ from the repository. """) } } def make_contrib(): Unit = { Isabelle_System.make_directory(Components.contrib(isabelle_dir)) File.write(Components.contrib(isabelle_dir, name = "README"), """This directory contains add-on components that contribute to the main Isabelle distribution. Separate licensing conditions apply, see each directory individually. """) } def bundle_info(platform: Platform.Family.Value): Bundle_Info = platform match { case Platform.Family.linux_arm => Bundle_Info(platform, "Linux (ARM)", dist_name + "_linux_arm.tar.gz") case Platform.Family.linux => Bundle_Info(platform, "Linux", dist_name + "_linux.tar.gz") case Platform.Family.macos => Bundle_Info(platform, "macOS", dist_name + "_macos.tar.gz") case Platform.Family.windows => Bundle_Info(platform, "Windows", dist_name + ".exe") } } sealed case class Bundle_Info( platform: Platform.Family.Value, platform_description: String, name: String) { def path: Path = Path.explode(name) } /** release archive **/ val ISABELLE: Path = Path.basic("Isabelle") val ISABELLE_ID: Path = Path.explode("etc/ISABELLE_ID") val ISABELLE_TAGS: Path = Path.explode("etc/ISABELLE_TAGS") val ISABELLE_IDENTIFIER: Path = Path.explode("etc/ISABELLE_IDENTIFIER") object Release_Archive { def make(bytes: Bytes, rename: String = ""): Release_Archive = { Isabelle_System.with_tmp_dir("tmp")(dir => Isabelle_System.with_tmp_file("archive", ext = "tar.gz")(archive_path => { val isabelle_dir = Isabelle_System.make_directory(dir + ISABELLE) Bytes.write(archive_path, bytes) execute_tar(isabelle_dir, "-xzf " + File.bash_path(archive_path), strip = 1) val id = File.read(isabelle_dir + ISABELLE_ID) val tags = File.read(isabelle_dir + ISABELLE_TAGS) val identifier = File.read(isabelle_dir + ISABELLE_IDENTIFIER) val (bytes1, identifier1) = if (rename.isEmpty || rename == identifier) (bytes, identifier) else { File.write(isabelle_dir + ISABELLE_IDENTIFIER, rename) Isabelle_System.move_file(isabelle_dir, dir + Path.basic(rename)) execute_tar(dir, "-czf " + File.bash_path(archive_path) + " " + Bash.string(rename)) (Bytes.read(archive_path), rename) } new Release_Archive(bytes1, id, tags, identifier1) }) ) } def read(path: Path, rename: String = ""): Release_Archive = make(Bytes.read(path), rename = rename) def get(url: String, rename: String = "", progress: Progress = new Progress): Release_Archive = { val bytes = if (Path.is_wellformed(url)) Bytes.read(Path.explode(url)) else Isabelle_System.download(url, progress = progress).bytes make(bytes, rename = rename) } } case class Release_Archive private[Build_Release]( bytes: Bytes, id: String, tags: String, identifier: String) { override def toString: String = identifier } /** generated content **/ /* bundled components */ class Bundled(platform: Option[Platform.Family.Value] = None) { def detect(s: String): Boolean = s.startsWith("#bundled") && !s.startsWith("#bundled ") def apply(name: String): String = "#bundled" + (platform match { case None => "" case Some(plat) => "-" + plat }) + ":" + name private val Pattern1 = ("""^#bundled:(.*)$""").r private val Pattern2 = ("""^#bundled-(.*):(.*)$""").r def unapply(s: String): Option[String] = s match { case Pattern1(name) => Some(name) case Pattern2(Platform.Family(plat), name) if platform == Some(plat) => Some(name) case _ => None } } def record_bundled_components(dir: Path): Unit = { val catalogs = List("main", "bundled").map((_, new Bundled())) ::: Platform.Family.list.flatMap(platform => List(platform.toString, "bundled-" + platform.toString). map((_, new Bundled(platform = Some(platform))))) File.append(Components.components(dir), terminate_lines("#bundled components" :: (for { (catalog, bundled) <- catalogs.iterator path = Components.admin(dir) + Path.basic(catalog) if path.is_file line <- split_lines(File.read(path)) - if line.nonEmpty && !line.startsWith("#") && !line.startsWith("jedit_build") + if line.nonEmpty && !line.startsWith("#") } yield bundled(line)).toList)) } def get_bundled_components(dir: Path, platform: Platform.Family.Value): (List[String], String) = { val Bundled = new Bundled(platform = Some(platform)) val components = - for { - Bundled(name) <- Components.read_components(dir) - if !name.startsWith("jedit_build") - } yield name + for { Bundled(name) <- Components.read_components(dir) } yield name val jdk_component = components.find(_.startsWith("jdk")) getOrElse error("Missing jdk component") (components, jdk_component) } def activate_components( dir: Path, platform: Platform.Family.Value, more_names: List[String]): Unit = { def contrib_name(name: String): String = Components.contrib(name = name).implode val Bundled = new Bundled(platform = Some(platform)) Components.write_components(dir, Components.read_components(dir).flatMap(line => line match { case Bundled(name) => if (Components.check_dir(Components.contrib(dir, name))) Some(contrib_name(name)) else None case _ => if (Bundled.detect(line)) None else Some(line) }) ::: more_names.map(contrib_name)) } /** build release **/ /* build heaps */ private def build_heaps( options: Options, platform: Platform.Family.Value, build_sessions: List[String], local_dir: Path): Unit = { val server_option = "build_host_" + platform.toString val ssh = options.string(server_option) match { case "" => if (Platform.family == platform) SSH.Local else error("Undefined option " + server_option + ": cannot build heaps") case SSH.Target(user, host) => SSH.open_session(options, host = host, user = user) case s => error("Malformed option " + server_option + ": " + quote(s)) } try { Isabelle_System.with_tmp_file("tmp", ext = "tar")(local_tmp_tar => { execute_tar(local_dir, "-cf " + File.bash_path(local_tmp_tar) + " .") ssh.with_tmp_dir(remote_dir => { val remote_tmp_tar = remote_dir + Path.basic("tmp.tar") ssh.write_file(remote_tmp_tar, local_tmp_tar) val remote_commands = List( "cd " + File.bash_path(remote_dir), "tar -xf tmp.tar", "bin/isabelle build -o system_heaps -b -- " + Bash.strings(build_sessions), "tar -cf tmp.tar heaps") ssh.execute(remote_commands.mkString(" && "), settings = false).check ssh.read_file(remote_tmp_tar, local_tmp_tar) }) execute_tar(local_dir, "-xf " + File.bash_path(local_tmp_tar)) }) } finally { ssh.close() } } /* Isabelle application */ def make_isabelle_options(path: Path, options: List[String], line_ending: String = "\n"): Unit = { val title = "# Java runtime options" File.write(path, (title :: options).map(_ + line_ending).mkString) } def make_isabelle_app( platform: Platform.Family.Value, isabelle_target: Path, isabelle_name: String, jdk_component: String, classpath: List[Path], dock_icon: Boolean = false): Unit = { val script = """#!/usr/bin/env bash # # Author: Makarius # # Main Isabelle application script. # minimal Isabelle environment ISABELLE_HOME="$(cd "$(dirname "$0")"; cd "$(pwd -P)/../.."; pwd)" source "$ISABELLE_HOME/lib/scripts/isabelle-platform" #paranoia settings -- avoid intrusion of alien options unset "_JAVA_OPTIONS" unset "JAVA_TOOL_OPTIONS" #paranoia settings -- avoid problems of Java/Swing versus XIM/IBus etc. unset XMODIFIERS COMPONENT="$ISABELLE_HOME/contrib/""" + jdk_component + """" source "$COMPONENT/etc/settings" # main declare -a JAVA_OPTIONS=($(grep -v '^#' "$ISABELLE_HOME/Isabelle.options")) "$ISABELLE_HOME/bin/isabelle" env "$ISABELLE_HOME/lib/scripts/java-gui-setup" exec "$ISABELLE_JDK_HOME/bin/java" \ "-Disabelle.root=$ISABELLE_HOME" "${JAVA_OPTIONS[@]}" \ -classpath """" + classpath.map(p => "$ISABELLE_HOME/" + p.implode).mkString(":") + """" \ "-splash:$ISABELLE_HOME/lib/logo/isabelle.gif" \ """ + (if (dock_icon) """"-Xdock:icon=$ISABELLE_HOME/lib/logo/isabelle_transparent-128.png" \ -""" else "") + """isabelle.Main "$@" +""" else "") + """isabelle.jedit.Main "$@" """ val script_path = isabelle_target + Path.explode("lib/scripts/Isabelle_app") File.write(script_path, script) File.set_executable(script_path, true) val component_dir = isabelle_target + Path.explode("contrib/Isabelle_app") Isabelle_System.move_file( component_dir + Path.explode(Platform.Family.standard(platform)) + Path.explode("Isabelle"), isabelle_target + Path.explode(isabelle_name)) Isabelle_System.rm_tree(component_dir) } def make_isabelle_plist(path: Path, isabelle_name: String, isabelle_rev: String): Unit = { File.write(path, """ CFBundleDevelopmentRegion English CFBundleIconFile isabelle.icns CFBundleIdentifier de.tum.in.isabelle CFBundleDisplayName """ + isabelle_name + """ CFBundleInfoDictionaryVersion 6.0 CFBundleName """ + isabelle_name + """ CFBundlePackageType APPL CFBundleShortVersionString """ + isabelle_name + """ CFBundleSignature ???? CFBundleVersion """ + isabelle_rev + """ NSHumanReadableCopyright LSMinimumSystemVersion 10.11 LSApplicationCategoryType public.app-category.developer-tools NSHighResolutionCapable true NSSupportsAutomaticGraphicsSwitching true CFBundleDocumentTypes CFBundleTypeExtensions thy CFBundleTypeIconFile theory.icns CFBundleTypeName Isabelle theory file CFBundleTypeRole Editor LSTypeIsPackage """) } /* main */ def use_release_archive( context: Release_Context, archive: Release_Archive, id: String = ""): Unit = { if (id.nonEmpty && id != archive.id) { error("Mismatch of release identification " + id + " vs. archive " + archive.id) } if (!context.isabelle_archive.is_file || Bytes.read(context.isabelle_archive) != archive.bytes) { Bytes.write(context.isabelle_archive, archive.bytes) } } def build_release_archive( context: Release_Context, version: String, parallel_jobs: Int = 1): Unit = { val progress = context.progress val hg = Mercurial.repository(Path.ISABELLE_HOME) val id = try { hg.id(version) } catch { case ERROR(msg) => cat_error("Bad repository version: " + version, msg) } if (context.isabelle_archive.is_file) { progress.echo_warning("Found existing release archive: " + context.isabelle_archive) use_release_archive(context, Release_Archive.read(context.isabelle_archive), id = id) } else { progress.echo_warning("Preparing release " + context.dist_name + " ...") Isabelle_System.new_directory(context.dist_dir) hg.archive(context.isabelle_dir.expand.implode, rev = id, options = "--type files") for (name <- List(".hg_archival.txt", ".hgtags", ".hgignore", "README_REPOSITORY")) { (context.isabelle_dir + Path.explode(name)).file.delete } File.write(context.isabelle_dir + ISABELLE_ID, id) File.write(context.isabelle_dir + ISABELLE_TAGS, hg.tags(rev = id)) File.write(context.isabelle_dir + ISABELLE_IDENTIFIER, context.dist_name) context.make_announce(id) context.make_contrib() execute(context.isabelle_dir, """find . -print | xargs chmod -f u+rw""") record_bundled_components(context.isabelle_dir) /* build tools and documentation */ val other_isabelle = context.other_isabelle(context.dist_dir) other_isabelle.init_settings( other_isabelle.init_components( components_base = context.components_base, catalogs = List("main"))) other_isabelle.resolve_components(echo = true) try { val export_classpath = "export CLASSPATH=" + Bash.string(other_isabelle.getenv("ISABELLE_CLASSPATH")) + "\n" other_isabelle.bash(export_classpath + "Admin/build all", echo = true).check other_isabelle.bash(export_classpath + "bin/isabelle jedit -b", echo = true).check } catch { case ERROR(msg) => cat_error("Failed to build tools:", msg) } try { other_isabelle.bash( "bin/isabelle build_doc -a -o system_heaps -j " + parallel_jobs, echo = true).check } catch { case ERROR(msg) => cat_error("Failed to build documentation:", msg) } other_isabelle.make_news() for (name <- List("Admin", "browser_info", "heaps")) { Isabelle_System.rm_tree(other_isabelle.isabelle_home + Path.explode(name)) } other_isabelle.cleanup() progress.echo_warning("Creating release archive " + context.isabelle_archive + " ...") execute(context.dist_dir, """chmod -R a+r . && chmod -R u+w . && chmod -R g=o .""") execute(context.dist_dir, """find . -type f "(" -name "*.thy" -o -name "*.ML" -o -name "*.scala" ")" -print | xargs chmod -f u-w""") execute_tar(context.dist_dir, "-czf " + File.bash_path(context.isabelle_archive) + " " + Bash.string(context.dist_name)) } } def default_platform_families: List[Platform.Family.Value] = Platform.Family.list0 def build_release( options: Options, context: Release_Context, afp_rev: String = "", platform_families: List[Platform.Family.Value] = default_platform_families, more_components: List[Path] = Nil, website: Option[Path] = None, build_sessions: List[String] = Nil, build_library: Boolean = false, parallel_jobs: Int = 1): Unit = { val progress = context.progress /* release directory */ val archive = Release_Archive.read(context.isabelle_archive) for (path <- List(context.isabelle, ISABELLE)) { Isabelle_System.rm_tree(context.dist_dir + path) } Isabelle_System.with_tmp_file("archive", ext = "tar.gz")(archive_path => { Bytes.write(archive_path, archive.bytes) val extract = List("README", "NEWS", "ANNOUNCE", "COPYRIGHT", "CONTRIBUTORS", "doc"). map(name => context.dist_name + "/" + name) execute_tar(context.dist_dir, "-xzf " + File.bash_path(archive_path) + " " + Bash.strings(extract)) }) Isabelle_System.symlink(Path.explode(context.dist_name), context.dist_dir + ISABELLE) /* make application bundles */ val bundle_infos = platform_families.map(context.bundle_info) for (bundle_info <- bundle_infos) { val isabelle_name = context.dist_name val platform = bundle_info.platform progress.echo("\nApplication bundle for " + platform) Isabelle_System.with_tmp_dir("build_release")(tmp_dir => { // release archive execute_tar(tmp_dir, "-xzf " + File.bash_path(context.isabelle_archive)) val other_isabelle = context.other_isabelle(tmp_dir) val isabelle_target = other_isabelle.isabelle_home // bundled components progress.echo("Bundled components:") val contrib_dir = Components.contrib(isabelle_target) val (bundled_components, jdk_component) = get_bundled_components(isabelle_target, platform) Components.resolve(context.components_base, bundled_components, target_dir = Some(contrib_dir), copy_dir = Some(context.dist_dir + Path.explode("contrib")), progress = progress) val more_components_names = more_components.map(Components.unpack(contrib_dir, _, progress = progress)) Components.purge(contrib_dir, platform) activate_components(isabelle_target, platform, more_components_names) // Java parameters val java_options: List[String] = (for { variable <- List( "ISABELLE_JAVA_SYSTEM_OPTIONS", "JEDIT_JAVA_SYSTEM_OPTIONS", "JEDIT_JAVA_OPTIONS") opt <- Word.explode(other_isabelle.getenv(variable)) } yield { val s = "-Dapple.awt.application.name=" if (opt.startsWith(s)) s + isabelle_name else opt }) ::: List("-Disabelle.jedit_server=" + isabelle_name) val classpath: List[Path] = { val base = isabelle_target.absolute - Path.split(other_isabelle.getenv("ISABELLE_CLASSPATH")).map(path => + Path.split(other_isabelle.setup_classpath()).map(path => { val abs_path = path.absolute File.relative_path(base, abs_path) match { case Some(rel_path) => rel_path case None => error("Bad ISABELLE_CLASSPATH element: " + abs_path) } - }) ::: List(Path.explode("src/Tools/jEdit/dist/jedit.jar")) + }) } val jedit_options = Path.explode("src/Tools/jEdit/etc/options") - val jedit_props = Path.explode("src/Tools/jEdit/dist/properties/jEdit.props") + val jedit_props = + Path.explode(other_isabelle.getenv("JEDIT_HOME") + "/properties/jEdit.props") // build heaps if (build_sessions.nonEmpty) { progress.echo("Building heaps " + commas_quote(build_sessions) + " ...") build_heaps(options, platform, build_sessions, isabelle_target) } // application bundling platform match { case Platform.Family.linux_arm | Platform.Family.linux => File.change(isabelle_target + jedit_options, _.replaceAll("jedit_reset_font_size : int =.*", "jedit_reset_font_size : int = 24")) File.change(isabelle_target + jedit_props, _.replaceAll("console.fontsize=.*", "console.fontsize=18") .replaceAll("helpviewer.fontsize=.*", "helpviewer.fontsize=18") .replaceAll("metal.primary.fontsize=.*", "metal.primary.fontsize=18") .replaceAll("metal.secondary.fontsize=.*", "metal.secondary.fontsize=18") .replaceAll("view.fontsize=.*", "view.fontsize=24") .replaceAll("view.gutter.fontsize=.*", "view.gutter.fontsize=16")) make_isabelle_options( isabelle_target + Path.explode("Isabelle.options"), java_options) make_isabelle_app(platform, isabelle_target, isabelle_name, jdk_component, classpath) progress.echo("Packaging " + bundle_info.name + " ...") execute_tar(tmp_dir, "-czf " + File.bash_path(context.dist_dir + bundle_info.path) + " " + Bash.string(isabelle_name)) case Platform.Family.macos => File.change(isabelle_target + jedit_props, _.replaceAll("delete-line.shortcut=.*", "delete-line.shortcut=C+d") .replaceAll("delete.shortcut2=.*", "delete.shortcut2=A+d")) // macOS application bundle val app_contents = isabelle_target + Path.explode("Contents") for (icon <- List("lib/logo/isabelle.icns", "lib/logo/theory.icns")) { Isabelle_System.copy_file(isabelle_target + Path.explode(icon), Isabelle_System.make_directory(app_contents + Path.explode("Resources"))) } make_isabelle_plist( app_contents + Path.explode("Info.plist"), isabelle_name, archive.id) make_isabelle_app(platform, isabelle_target, isabelle_name, jdk_component, classpath, dock_icon = true) val isabelle_options = Path.explode("Isabelle.options") make_isabelle_options( isabelle_target + isabelle_options, java_options ::: List("-Disabelle.app=true")) // application archive progress.echo("Packaging " + bundle_info.name + " ...") val isabelle_app = Path.explode(isabelle_name + ".app") Isabelle_System.move_file(tmp_dir + Path.explode(isabelle_name), tmp_dir + isabelle_app) execute_tar(tmp_dir, "-czf " + File.bash_path(context.dist_dir + bundle_info.path) + " " + File.bash_path(isabelle_app)) case Platform.Family.windows => File.change(isabelle_target + jedit_props, _.replaceAll("foldPainter=.*", "foldPainter=Square")) // application launcher Isabelle_System.move_file(isabelle_target + Path.explode("contrib/windows_app"), tmp_dir) val app_template = Path.explode("~~/Admin/Windows/launch4j") make_isabelle_options( isabelle_target + Path.explode(isabelle_name + ".l4j.ini"), java_options, line_ending = "\r\n") val isabelle_xml = Path.explode("isabelle.xml") val isabelle_exe = bundle_info.path File.write(tmp_dir + isabelle_xml, File.read(app_template + isabelle_xml) .replace("{ISABELLE_NAME}", isabelle_name) .replace("{OUTFILE}", File.platform_path(isabelle_target + isabelle_exe)) .replace("{ICON}", File.platform_path(app_template + Path.explode("isabelle_transparent.ico"))) .replace("{SPLASH}", File.platform_path(app_template + Path.explode("isabelle.bmp"))) .replace("{CLASSPATH}", cat_lines(classpath.map(cp => " %EXEDIR%\\" + File.platform_path(cp).replace('/', '\\') + ""))) .replace("\\jdk\\", "\\" + jdk_component + "\\")) execute(tmp_dir, "\"windows_app/launch4j-${ISABELLE_PLATFORM_FAMILY}/launch4j\" isabelle.xml") Isabelle_System.copy_file(app_template + Path.explode("manifest.xml"), isabelle_target + isabelle_exe.ext("manifest")) // Cygwin setup val cygwin_template = Path.explode("~~/Admin/Windows/Cygwin") Isabelle_System.copy_file(cygwin_template + Path.explode("Cygwin-Terminal.bat"), isabelle_target) val cygwin_mirror = File.read(isabelle_target + Path.explode("contrib/cygwin/isabelle/cygwin_mirror")) val cygwin_bat = Path.explode("Cygwin-Setup.bat") File.write(isabelle_target + cygwin_bat, File.read(cygwin_template + cygwin_bat).replace("{MIRROR}", cygwin_mirror)) File.set_executable(isabelle_target + cygwin_bat, true) for (name <- List("isabelle/postinstall", "isabelle/rebaseall")) { val path = Path.explode(name) Isabelle_System.copy_file(cygwin_template + path, isabelle_target + Path.explode("contrib/cygwin") + path) } execute(isabelle_target, """find . -type f -not -name "*.exe" -not -name "*.dll" """ + (if (Platform.is_macos) "-perm +100" else "-executable") + " -print0 > contrib/cygwin/isabelle/executables") execute(isabelle_target, """find . -type l -exec echo "{}" ";" -exec readlink "{}" ";" """ + """> contrib/cygwin/isabelle/symlinks""") execute(isabelle_target, """find . -type l -exec rm "{}" ";" """) File.write(isabelle_target + Path.explode("contrib/cygwin/isabelle/uninitialized"), "") // executable archive (self-extracting 7z) val archive_name = isabelle_name + ".7z" val exe_archive = tmp_dir + Path.explode(archive_name) exe_archive.file.delete progress.echo("Packaging " + archive_name + " ...") execute(tmp_dir, "7z -y -bd a " + File.bash_path(exe_archive) + " " + Bash.string(isabelle_name)) if (!exe_archive.is_file) error("Failed to create archive: " + exe_archive) val sfx_exe = tmp_dir + Path.explode("windows_app/7zsd_All_x64.sfx") val sfx_txt = File.read(Path.explode("~~/Admin/Windows/Installer/sfx.txt")) .replace("{ISABELLE_NAME}", isabelle_name) Bytes.write(context.dist_dir + isabelle_exe, Bytes.read(sfx_exe) + Bytes(sfx_txt) + Bytes.read(exe_archive)) File.set_executable(context.dist_dir + isabelle_exe, true) } }) progress.echo("DONE") } /* minimal website */ for (dir <- website) { val website_platform_bundles = for { bundle_info <- bundle_infos if (context.dist_dir + bundle_info.path).is_file } yield (bundle_info.name, bundle_info) val isabelle_link = HTML.link(Isabelle_System.isabelle_repository.changeset(archive.id), HTML.text("Isabelle/" + archive.id)) val afp_link = HTML.link(Isabelle_System.afp_repository.changeset(afp_rev), HTML.text("AFP/" + afp_rev)) HTML.write_document(dir, "index.html", List(HTML.title(context.dist_name)), List( HTML.section(context.dist_name), HTML.subsection("Downloads"), HTML.itemize( List(HTML.link(context.dist_name + ".tar.gz", HTML.text("Source archive"))) :: website_platform_bundles.map({ case (bundle, bundle_info) => List(HTML.link(bundle, HTML.text(bundle_info.platform_description + " bundle"))) })), HTML.subsection("Repositories"), HTML.itemize( List(List(isabelle_link)) ::: (if (afp_rev == "") Nil else List(List(afp_link)))))) Isabelle_System.copy_file(context.isabelle_archive, dir) for ((bundle, _) <- website_platform_bundles) { Isabelle_System.copy_file(context.dist_dir + Path.explode(bundle), dir) } } /* HTML library */ if (build_library) { if (context.isabelle_library_archive.is_file) { progress.echo_warning("Library archive already exists: " + context.isabelle_library_archive) } else { Isabelle_System.with_tmp_dir("build_release")(tmp_dir => { val bundle = context.dist_dir + Path.explode(context.dist_name + "_" + Platform.family + ".tar.gz") execute_tar(tmp_dir, "-xzf " + File.bash_path(bundle)) val other_isabelle = context.other_isabelle(tmp_dir) Isabelle_System.make_directory(other_isabelle.etc) File.write(other_isabelle.etc_preferences, "ML_system_64 = true\n") other_isabelle.bash("bin/isabelle build -f -j " + parallel_jobs + " -o browser_info -o document=pdf -o document_variants=document:outline=/proof,/ML" + " -o system_heaps -c -a -d '~~/src/Benchmarks'", echo = true).check other_isabelle.isabelle_home_user.file.delete execute(tmp_dir, "chmod -R a+r " + Bash.string(context.dist_name)) execute(tmp_dir, "chmod -R g=o " + Bash.string(context.dist_name)) execute_tar(tmp_dir, "-czf " + File.bash_path(context.isabelle_library_archive) + " " + Bash.string(context.dist_name + "/browser_info")) }) } } } /** command line entry point **/ def main(args: Array[String]): Unit = { Command_Line.tool { var afp_rev = "" var components_base: Path = Components.default_components_base var target_dir = Path.current var release_name = "" var source_archive = "" var website: Option[Path] = None var build_sessions: List[String] = Nil var more_components: List[Path] = Nil var parallel_jobs = 1 var build_library = false var options = Options.init() var platform_families = default_platform_families var rev = "" val getopts = Getopts(""" Usage: Admin/build_release [OPTIONS] Options are: -A REV corresponding AFP changeset id -C DIR base directory for Isabelle components (default: """ + Components.default_components_base + """) -D DIR target directory (default ".") -R RELEASE explicit release name -S ARCHIVE use existing source archive (file or URL) -W WEBSITE produce minimal website in given directory -b SESSIONS build platform-specific session images (separated by commas) -c ARCHIVE clean bundling with additional component .tar.gz archive -j INT maximum number of parallel jobs (default 1) -l build library -o OPTION override Isabelle system OPTION (via NAME=VAL or NAME) -p NAMES platform families (default: """ + default_platform_families.mkString(",") + """) -r REV Mercurial changeset id (default: ARCHIVE or RELEASE or tip) Build Isabelle release in base directory, using the local repository clone. """, "A:" -> (arg => afp_rev = arg), "C:" -> (arg => components_base = Path.explode(arg)), "D:" -> (arg => target_dir = Path.explode(arg)), "R:" -> (arg => release_name = arg), "S:" -> (arg => source_archive = arg), "W:" -> (arg => website = Some(Path.explode(arg))), "b:" -> (arg => build_sessions = space_explode(',', arg)), "c:" -> (arg => { val path = Path.explode(arg) Components.Archive.get_name(path.file_name) more_components = more_components ::: List(path) }), "j:" -> (arg => parallel_jobs = Value.Int.parse(arg)), "l" -> (_ => build_library = true), "o:" -> (arg => options = options + arg), "p:" -> (arg => platform_families = space_explode(',', arg).map(Platform.Family.parse)), "r:" -> (arg => rev = arg)) val more_args = getopts(args) if (more_args.nonEmpty) getopts.usage() if (platform_families.contains(Platform.Family.windows) && !Isabelle_System.bash("7z i").ok) error("Building for windows requires 7z") val progress = new Console_Progress() def make_context(name: String): Release_Context = Release_Context(target_dir, release_name = name, components_base = components_base, progress = progress) val context = if (source_archive.isEmpty) { val context = make_context(release_name) val version = proper_string(rev) orElse proper_string(release_name) getOrElse "tip" build_release_archive(context, version, parallel_jobs = parallel_jobs) context } else { val archive = Release_Archive.get(source_archive, rename = release_name, progress = progress) val context = make_context(archive.identifier) Isabelle_System.make_directory(context.dist_dir) use_release_archive(context, archive, id = rev) context } build_release(options, context, afp_rev = afp_rev, platform_families = platform_families, more_components = more_components, build_sessions = build_sessions, build_library = build_library, parallel_jobs = parallel_jobs, website = website) } } } diff --git a/src/Pure/Admin/other_isabelle.scala b/src/Pure/Admin/other_isabelle.scala --- a/src/Pure/Admin/other_isabelle.scala +++ b/src/Pure/Admin/other_isabelle.scala @@ -1,134 +1,138 @@ /* Title: Pure/Admin/other_isabelle.scala Author: Makarius Manage other Isabelle distributions. */ package isabelle object Other_Isabelle { def apply(isabelle_home: Path, isabelle_identifier: String = "", user_home: Path = Path.USER_HOME, progress: Progress = new Progress): Other_Isabelle = new Other_Isabelle(isabelle_home.canonical, isabelle_identifier, user_home, progress) } class Other_Isabelle( val isabelle_home: Path, val isabelle_identifier: String, user_home: Path, progress: Progress) { other_isabelle => override def toString: String = isabelle_home.toString if (proper_string(System.getenv("ISABELLE_SETTINGS_PRESENT")).isDefined) error("Cannot initialize with enclosing ISABELLE_SETTINGS_PRESENT") /* static system */ def bash( script: String, redirect: Boolean = false, echo: Boolean = false, strict: Boolean = true): Process_Result = progress.bash( "export USER_HOME=" + File.bash_path(user_home) + "\n" + Isabelle_System.export_isabelle_identifier(isabelle_identifier) + script, env = null, cwd = isabelle_home.file, redirect = redirect, echo = echo, strict = strict) def apply( cmdline: String, redirect: Boolean = false, echo: Boolean = false, strict: Boolean = true): Process_Result = bash("bin/isabelle " + cmdline, redirect = redirect, echo = echo, strict = strict) def resolve_components(echo: Boolean): Unit = other_isabelle( "env ISABELLE_TOOLS=" + Bash.string(Isabelle_System.getenv("ISABELLE_TOOLS")) + " isabelle components -a", redirect = true, echo = echo).check def getenv(name: String): String = other_isabelle("getenv -b " + Bash.string(name)).check.out + def setup_classpath(): String = + other_isabelle("env bash -c " + + Bash.string("isabelle_setup_classpath && isabelle getenv -b ISABELLE_CLASSPATH")).check.out + val isabelle_home_user: Path = Path.explode(getenv("ISABELLE_HOME_USER")) val etc: Path = isabelle_home_user + Path.explode("etc") val etc_settings: Path = etc + Path.explode("settings") val etc_preferences: Path = etc + Path.explode("preferences") /* NEWS */ def make_news(): Unit = { val doc_dir = isabelle_home + Path.explode("doc") val fonts_dir = Isabelle_System.make_directory(doc_dir + Path.explode("fonts")) Isabelle_Fonts.make_entries(getenv = getenv, hidden = true). foreach(entry => Isabelle_System.copy_file(entry.path, fonts_dir)) HTML.write_document(doc_dir, "NEWS.html", List(HTML.title("NEWS")), List( HTML.chapter("NEWS"), HTML.source(Symbol.decode(File.read(isabelle_home + Path.explode("NEWS")))))) } /* components */ def init_components( component_repository: String = Components.default_component_repository, components_base: Path = Components.default_components_base, catalogs: List[String] = Nil, components: List[String] = Nil): List[String] = { val dir = Components.admin(isabelle_home) ("ISABELLE_COMPONENT_REPOSITORY=" + Bash.string(component_repository)) :: catalogs.map(name => "init_components " + File.bash_path(components_base) + " " + File.bash_path(dir + Path.basic(name))) ::: components.map(name => "init_component " + File.bash_path(components_base + Path.basic(name))) } /* settings */ def clean_settings(): Boolean = if (!etc_settings.is_file) true else if (File.read(etc_settings).startsWith("# generated by Isabelle")) { etc_settings.file.delete; true } else false def init_settings(settings: List[String]): Unit = { if (!clean_settings()) error("Cannot proceed with existing user settings file: " + etc_settings) Isabelle_System.make_directory(etc_settings.dir) File.write(etc_settings, "# generated by Isabelle " + Date.now() + "\n" + "#-*- shell-script -*- :mode=shellscript:\n" + settings.mkString("\n", "\n", "\n")) } /* cleanup */ def cleanup(): Unit = { clean_settings() etc.file.delete isabelle_home_user.file.delete } } diff --git a/src/Pure/System/isabelle_system.scala b/src/Pure/System/isabelle_system.scala --- a/src/Pure/System/isabelle_system.scala +++ b/src/Pure/System/isabelle_system.scala @@ -1,478 +1,489 @@ /* Title: Pure/System/isabelle_system.scala Author: Makarius Miscellaneous Isabelle system operations. */ package isabelle import java.util.{Map => JMap} import java.io.{File => JFile, IOException} import java.nio.file.{Path => JPath, Files, SimpleFileVisitor, FileVisitResult, StandardCopyOption, FileSystemException} import java.nio.file.attribute.BasicFileAttributes +import scala.jdk.CollectionConverters._ + object Isabelle_System { /* settings */ def settings(): JMap[String, String] = isabelle.setup.Environment.settings() def getenv(name: String, env: JMap[String, String] = settings()): String = Option(env.get(name)).getOrElse("") def getenv_strict(name: String, env: JMap[String, String] = settings()): String = proper_string(getenv(name, env)) getOrElse error("Undefined Isabelle environment variable: " + quote(name)) /* services */ abstract class Service @volatile private var _services: Option[List[Class[Service]]] = None def services(): List[Class[Service]] = { if (_services.isEmpty) init() // unsynchronized check _services.get } def make_services[C](c: Class[C]): List[C] = for { c1 <- services() if Library.is_subclass(c1, c) } yield c1.getDeclaredConstructor().newInstance().asInstanceOf[C] /* init settings + services */ + def make_services(): List[Class[Service]] = + { + def make(where: String, names: List[String]): List[Class[Service]] = + { + for (name <- names) yield { + def err(msg: String): Nothing = + error("Bad Isabelle/Scala service " + quote(name) + " in " + where + "\n" + msg) + try { Class.forName(name).asInstanceOf[Class[Service]] } + catch { + case _: ClassNotFoundException => err("Class not found") + case exn: Throwable => err(Exn.message(exn)) + } + } + } + + def from_env(variable: String): List[Class[Service]] = + make(quote(variable), space_explode(':', getenv_strict(variable))) + + def from_jar(platform_jar: String): List[Class[Service]] = + make(quote(platform_jar), + isabelle.setup.Build.get_services(JPath.of(platform_jar)).asScala.toList) + + from_env("ISABELLE_SCALA_SERVICES") ::: Scala.class_path().flatMap(from_jar) + } + def init(isabelle_root: String = "", cygwin_root: String = ""): Unit = { isabelle.setup.Environment.init(isabelle_root, cygwin_root) - synchronized { - if (_services.isEmpty) { - val variable = "ISABELLE_SCALA_SERVICES" - val services = - for (name <- space_explode(':', getenv_strict(variable))) - yield { - def err(msg: String): Nothing = - error("Bad entry " + quote(name) + " in " + variable + "\n" + msg) - try { Class.forName(name).asInstanceOf[Class[Service]] } - catch { - case _: ClassNotFoundException => err("Class not found") - case exn: Throwable => err(Exn.message(exn)) - } - } - _services = Some(services) - } - } + synchronized { if (_services.isEmpty) { _services = Some(make_services()) } } } /* getetc -- static distribution parameters */ def getetc(name: String, root: Path = Path.ISABELLE_HOME): Option[String] = { val path = root + Path.basic("etc") + Path.basic(name) if (path.is_file) { Library.trim_split_lines(File.read(path)) match { case Nil => None case List(s) => Some(s) case _ => error("Single line expected in " + path.absolute) } } else None } /* Isabelle distribution identification */ def isabelle_id(root: Path = Path.ISABELLE_HOME): String = getetc("ISABELLE_ID", root = root) orElse Mercurial.archive_id(root) getOrElse { if (Mercurial.is_repository(root)) Mercurial.repository(root).parent() else error("Failed to identify Isabelle distribution " + root) } object Isabelle_Id extends Scala.Fun_String("isabelle_id") { val here = Scala_Project.here def apply(arg: String): String = isabelle_id() } def isabelle_tags(root: Path = Path.ISABELLE_HOME): String = getetc("ISABELLE_TAGS", root = root) orElse Mercurial.archive_tags(root) getOrElse { if (Mercurial.is_repository(root)) { val hg = Mercurial.repository(root) hg.tags(rev = hg.parent()) } else "" } def isabelle_identifier(): Option[String] = proper_string(getenv("ISABELLE_IDENTIFIER")) def isabelle_heading(): String = isabelle_identifier() match { case None => "" case Some(version) => " (" + version + ")" } def isabelle_name(): String = getenv_strict("ISABELLE_NAME") def identification(): String = "Isabelle/" + isabelle_id() + isabelle_heading() /** file-system operations **/ /* scala functions */ private def apply_paths(args: List[String], fun: List[Path] => Unit): List[String] = { fun(args.map(Path.explode)); Nil } private def apply_paths1(args: List[String], fun: Path => Unit): List[String] = apply_paths(args, { case List(path) => fun(path) }) private def apply_paths2(args: List[String], fun: (Path, Path) => Unit): List[String] = apply_paths(args, { case List(path1, path2) => fun(path1, path2) }) private def apply_paths3(args: List[String], fun: (Path, Path, Path) => Unit): List[String] = apply_paths(args, { case List(path1, path2, path3) => fun(path1, path2, path3) }) /* permissions */ def chmod(arg: String, path: Path): Unit = bash("chmod " + arg + " " + File.bash_path(path)).check def chown(arg: String, path: Path): Unit = bash("chown " + arg + " " + File.bash_path(path)).check /* directories */ def make_directory(path: Path): Path = { if (!path.is_dir) { try { Files.createDirectories(path.java_path) } catch { case ERROR(_) => error("Failed to create directory: " + path.absolute) } } path } def new_directory(path: Path): Path = if (path.is_dir) error("Directory already exists: " + path.absolute) else make_directory(path) def copy_dir(dir1: Path, dir2: Path): Unit = { val res = bash("cp -a " + File.bash_path(dir1) + " " + File.bash_path(dir2)) if (!res.ok) { cat_error("Failed to copy directory " + dir1.absolute + " to " + dir2.absolute, res.err) } } object Make_Directory extends Scala.Fun_Strings("make_directory") { val here = Scala_Project.here def apply(args: List[String]): List[String] = apply_paths1(args, make_directory) } object Copy_Dir extends Scala.Fun_Strings("copy_dir") { val here = Scala_Project.here def apply(args: List[String]): List[String] = apply_paths2(args, copy_dir) } /* copy files */ def copy_file(src: JFile, dst: JFile): Unit = { val target = if (dst.isDirectory) new JFile(dst, src.getName) else dst if (!File.eq(src, target)) { try { Files.copy(src.toPath, target.toPath, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING) } catch { case ERROR(msg) => cat_error("Failed to copy file " + File.path(src).absolute + " to " + File.path(dst).absolute, msg) } } } def copy_file(src: Path, dst: Path): Unit = copy_file(src.file, dst.file) def copy_file_base(base_dir: Path, src: Path, target_dir: Path): Unit = { val src1 = src.expand val src1_dir = src1.dir if (!src1.starts_basic) error("Illegal path specification " + src1 + " beyond base directory") copy_file(base_dir + src1, Isabelle_System.make_directory(target_dir + src1_dir)) } object Copy_File extends Scala.Fun_Strings("copy_file") { val here = Scala_Project.here def apply(args: List[String]): List[String] = apply_paths2(args, copy_file) } object Copy_File_Base extends Scala.Fun_Strings("copy_file_base") { val here = Scala_Project.here def apply(args: List[String]): List[String] = apply_paths3(args, copy_file_base) } /* move files */ def move_file(src: JFile, dst: JFile): Unit = { val target = if (dst.isDirectory) new JFile(dst, src.getName) else dst if (!File.eq(src, target)) Files.move(src.toPath, target.toPath, StandardCopyOption.REPLACE_EXISTING) } def move_file(src: Path, dst: Path): Unit = move_file(src.file, dst.file) /* symbolic link */ def symlink(src: Path, dst: Path, force: Boolean = false): Unit = { val src_file = src.file val dst_file = dst.file val target = if (dst_file.isDirectory) new JFile(dst_file, src_file.getName) else dst_file if (force) target.delete def cygwin_link(): Unit = isabelle.setup.Environment.cygwin_link(File.standard_path(src), target) try { Files.createSymbolicLink(target.toPath, src_file.toPath) } catch { case _: UnsupportedOperationException if Platform.is_windows => cygwin_link() case _: FileSystemException if Platform.is_windows => cygwin_link() } } /* tmp files */ def isabelle_tmp_prefix(): JFile = { val path = Path.explode("$ISABELLE_TMP_PREFIX") path.file.mkdirs // low-level mkdirs to avoid recursion via Isabelle environment File.platform_file(path) } def tmp_file(name: String, ext: String = "", base_dir: JFile = isabelle_tmp_prefix()): JFile = { val suffix = if (ext == "") "" else "." + ext val file = Files.createTempFile(base_dir.toPath, name, suffix).toFile file.deleteOnExit file } def with_tmp_file[A](name: String, ext: String = "")(body: Path => A): A = { val file = tmp_file(name, ext) try { body(File.path(file)) } finally { file.delete } } /* tmp dirs */ def rm_tree(root: JFile): Unit = { root.delete if (root.isDirectory) { Files.walkFileTree(root.toPath, new SimpleFileVisitor[JPath] { override def visitFile(file: JPath, attrs: BasicFileAttributes): FileVisitResult = { try { Files.deleteIfExists(file) } catch { case _: IOException => } FileVisitResult.CONTINUE } override def postVisitDirectory(dir: JPath, e: IOException): FileVisitResult = { if (e == null) { try { Files.deleteIfExists(dir) } catch { case _: IOException => } FileVisitResult.CONTINUE } else throw e } } ) } } def rm_tree(root: Path): Unit = rm_tree(root.file) object Rm_Tree extends Scala.Fun_Strings("rm_tree") { val here = Scala_Project.here def apply(args: List[String]): List[String] = apply_paths1(args, rm_tree) } def tmp_dir(name: String, base_dir: JFile = isabelle_tmp_prefix()): JFile = { val dir = Files.createTempDirectory(base_dir.toPath, name).toFile dir.deleteOnExit dir } def with_tmp_dir[A](name: String)(body: Path => A): A = { val dir = tmp_dir(name) try { body(File.path(dir)) } finally { rm_tree(dir) } } /* quasi-atomic update of directory */ def update_directory(dir: Path, f: Path => Unit): Unit = { val new_dir = dir.ext("new") val old_dir = dir.ext("old") rm_tree(new_dir) rm_tree(old_dir) f(new_dir) if (dir.is_dir) move_file(dir, old_dir) move_file(new_dir, dir) rm_tree(old_dir) } /** external processes **/ /* GNU bash */ def bash(script: String, cwd: JFile = null, env: JMap[String, String] = settings(), redirect: Boolean = false, progress_stdout: String => Unit = (_: String) => (), progress_stderr: String => Unit = (_: String) => (), watchdog: Option[Bash.Watchdog] = None, strict: Boolean = true, cleanup: () => Unit = () => ()): Process_Result = { Bash.process(script, cwd = cwd, env = env, redirect = redirect, cleanup = cleanup). result(progress_stdout = progress_stdout, progress_stderr = progress_stderr, watchdog = watchdog, strict = strict) } private lazy val gnutar_check: Boolean = try { bash("tar --version").check.out.containsSlice("GNU tar") || error("") } catch { case ERROR(_) => false } def gnutar( args: String, dir: Path = Path.current, original_owner: Boolean = false, strip: Int = 0, redirect: Boolean = false): Process_Result = { val options = (if (dir.is_current) "" else "-C " + File.bash_path(dir) + " ") + (if (original_owner) "" else "--owner=root --group=staff ") + (if (strip <= 0) "" else "--strip-components=" + strip + " ") if (gnutar_check) bash("tar " + options + args, redirect = redirect) else error("Expected to find GNU tar executable") } def require_command(cmd: String, test: String = "--version"): Unit = { if (!bash(Bash.string(cmd) + " " + test).ok) error("Missing system command: " + quote(cmd)) } def hostname(): String = bash("hostname -s").check.out def open(arg: String): Unit = bash("exec \"$ISABELLE_OPEN\" " + Bash.string(arg) + " >/dev/null 2>/dev/null &") def pdf_viewer(arg: Path): Unit = bash("exec \"$PDF_VIEWER\" " + File.bash_path(arg) + " >/dev/null 2>/dev/null &") def open_external_file(name: String): Boolean = { val ext = Library.take_suffix((c: Char) => c != '.', name.toList)._2.mkString val external = ext.nonEmpty && Library.space_explode(':', getenv("ISABELLE_EXTERNAL_FILES")).contains(ext) if (external) { if (ext == "pdf" && Path.is_wellformed(name)) pdf_viewer(Path.explode(name)) else open(name) } external } def export_isabelle_identifier(isabelle_identifier: String): String = if (isabelle_identifier == "") "" else "export ISABELLE_IDENTIFIER=" + Bash.string(isabelle_identifier) + "\n" /** Isabelle resources **/ /* repository clone with Admin */ def admin(): Boolean = Path.explode("~~/Admin").is_dir /* default logic */ def default_logic(args: String*): String = { args.find(_ != "") match { case Some(logic) => logic case None => getenv_strict("ISABELLE_LOGIC") } } /* download file */ def download(url_name: String, progress: Progress = new Progress): HTTP.Content = { val url = Url(url_name) progress.echo("Getting " + quote(url_name)) try { HTTP.Client.get(url) } catch { case ERROR(msg) => cat_error("Failed to download " + quote(url_name), msg) } } def download_file(url_name: String, file: Path, progress: Progress = new Progress): Unit = Bytes.write(file, download(url_name, progress = progress).bytes) object Download extends Scala.Fun("download", thread = true) { val here = Scala_Project.here override def invoke(args: List[Bytes]): List[Bytes] = args match { case List(url) => List(download(url.text).bytes) } } /* repositories */ val isabelle_repository: Mercurial.Server = Mercurial.Server("https://isabelle.sketis.net/repos/isabelle") val afp_repository: Mercurial.Server = Mercurial.Server("https://isabelle.sketis.net/repos/afp-devel") def official_releases(): List[String] = Library.trim_split_lines( isabelle_repository.read_file(Path.explode("Admin/Release/official"))) } diff --git a/src/Pure/System/scala.scala b/src/Pure/System/scala.scala --- a/src/Pure/System/scala.scala +++ b/src/Pure/System/scala.scala @@ -1,284 +1,285 @@ /* Title: Pure/System/scala.scala Author: Makarius Support for Scala at runtime. */ package isabelle import java.io.{File => JFile, StringWriter, PrintWriter} import scala.tools.nsc.{GenericRunnerSettings, ConsoleWriter, NewLinePrintWriter} import scala.tools.nsc.interpreter.{IMain, Results} import scala.tools.nsc.interpreter.shell.ReplReporterImpl object Scala { /** registered functions **/ abstract class Fun(val name: String, val thread: Boolean = false) { override def toString: String = name def multi: Boolean = true def position: Properties.T = here.position def here: Scala_Project.Here def invoke(args: List[Bytes]): List[Bytes] } abstract class Fun_Strings(name: String, thread: Boolean = false) extends Fun(name, thread = thread) { override def invoke(args: List[Bytes]): List[Bytes] = apply(args.map(_.text)).map(Bytes.apply) def apply(args: List[String]): List[String] } abstract class Fun_String(name: String, thread: Boolean = false) extends Fun_Strings(name, thread = thread) { override def multi: Boolean = false override def apply(args: List[String]): List[String] = List(apply(Library.the_single(args))) def apply(arg: String): String } class Functions(val functions: Fun*) extends Isabelle_System.Service lazy val functions: List[Fun] = Isabelle_System.make_services(classOf[Functions]).flatMap(_.functions) /** demo functions **/ object Echo extends Fun_String("echo") { val here = Scala_Project.here def apply(arg: String): String = arg } object Sleep extends Fun_String("sleep") { val here = Scala_Project.here def apply(seconds: String): String = { val t = seconds match { case Value.Double(s) => Time.seconds(s) case _ => error("Malformed argument: " + quote(seconds)) } val t0 = Time.now() t.sleep() val t1 = Time.now() (t1 - t0).toString } } /** compiler **/ + def class_path(): List[String] = + Library.distinct( + for { + prop <- List("isabelle.scala.classpath", "java.class.path") + elems = System.getProperty(prop, "") if elems.nonEmpty + elem <- space_explode(JFile.pathSeparatorChar, elems) if elem.nonEmpty + } yield elem) + object Compiler { def context( error: String => Unit = Exn.error, jar_dirs: List[JFile] = Nil): Context = { def find_jars(dir: JFile): List[String] = File.find_files(dir, file => file.getName.endsWith(".jar")). map(File.absolute_name) - val class_path = - for { - prop <- List("isabelle.scala.classpath", "java.class.path") - path = System.getProperty(prop, "") if path != "\"\"" - elem <- space_explode(JFile.pathSeparatorChar, path) - } yield elem - val settings = new GenericRunnerSettings(error) settings.classpath.value = - (class_path ::: jar_dirs.flatMap(find_jars)).mkString(JFile.pathSeparator) + (class_path() ::: jar_dirs.flatMap(find_jars)).mkString(JFile.pathSeparator) new Context(settings) } def default_print_writer: PrintWriter = new NewLinePrintWriter(new ConsoleWriter, true) class Context private [Compiler](val settings: GenericRunnerSettings) { override def toString: String = settings.toString def interpreter( print_writer: PrintWriter = default_print_writer, class_loader: ClassLoader = null): IMain = { new IMain(settings, new ReplReporterImpl(settings, print_writer)) { override def parentClassLoader: ClassLoader = if (class_loader == null) super.parentClassLoader else class_loader } } def toplevel(interpret: Boolean, source: String): List[String] = { val out = new StringWriter val interp = interpreter(new PrintWriter(out)) val marker = '\u000b' val ok = interp.withLabel(marker.toString) { if (interpret) interp.interpret(source) == Results.Success else (new interp.ReadEvalPrint).compile(source) } out.close() val Error = """(?s)^\S* error: (.*)$""".r val errors = space_explode(marker, Library.strip_ansi_color(out.toString)). collect({ case Error(msg) => "Scala error: " + Library.trim_line(msg) }) if (!ok && errors.isEmpty) List("Error") else errors } } } object Toplevel extends Fun_String("scala_toplevel") { val here = Scala_Project.here def apply(arg: String): String = { val (interpret, source) = YXML.parse_body(arg) match { case Nil => (false, "") case List(XML.Text(source)) => (false, source) case body => import XML.Decode._; pair(bool, string)(body) } val errors = try { Compiler.context().toplevel(interpret, source) } catch { case ERROR(msg) => List(msg) } locally { import XML.Encode._; YXML.string_of_body(list(string)(errors)) } } } /** invoke Scala functions from ML **/ /* invoke function */ object Tag extends Enumeration { val NULL, OK, ERROR, FAIL, INTERRUPT = Value } def function_thread(name: String): Boolean = functions.find(fun => fun.name == name) match { case Some(fun) => fun.thread case None => false } def function_body(name: String, args: List[Bytes]): (Tag.Value, List[Bytes]) = functions.find(fun => fun.name == name) match { case Some(fun) => Exn.capture { fun.invoke(args) } match { case Exn.Res(null) => (Tag.NULL, Nil) case Exn.Res(res) => (Tag.OK, res) case Exn.Exn(Exn.Interrupt()) => (Tag.INTERRUPT, Nil) case Exn.Exn(e) => (Tag.ERROR, List(Bytes(Exn.message(e)))) } case None => (Tag.FAIL, List(Bytes("Unknown Isabelle/Scala function: " + quote(name)))) } /* protocol handler */ class Handler extends Session.Protocol_Handler { private var session: Session = null private var futures = Map.empty[String, Future[Unit]] override def init(session: Session): Unit = synchronized { this.session = session } override def exit(): Unit = synchronized { for ((id, future) <- futures) cancel(id, future) futures = Map.empty } private def result(id: String, tag: Scala.Tag.Value, res: List[Bytes]): Unit = synchronized { if (futures.isDefinedAt(id)) { session.protocol_command_raw("Scala.result", Bytes(id) :: Bytes(tag.id.toString) :: res) futures -= id } } private def cancel(id: String, future: Future[Unit]): Unit = { future.cancel() result(id, Scala.Tag.INTERRUPT, Nil) } private def invoke_scala(msg: Prover.Protocol_Output): Boolean = synchronized { msg.properties match { case Markup.Invoke_Scala(name, id) => def body: Unit = { val (tag, res) = Scala.function_body(name, msg.chunks) result(id, tag, res) } val future = if (Scala.function_thread(name)) { Future.thread(name = Isabelle_Thread.make_name(base = "invoke_scala"))(body) } else Future.fork(body) futures += (id -> future) true case _ => false } } private def cancel_scala(msg: Prover.Protocol_Output): Boolean = synchronized { msg.properties match { case Markup.Cancel_Scala(id) => futures.get(id) match { case Some(future) => cancel(id, future) case None => } true case _ => false } } override val functions = List( Markup.Invoke_Scala.name -> invoke_scala, Markup.Cancel_Scala.name -> cancel_scala) } } class Scala_Functions extends Scala.Functions( Scala.Echo, Scala.Sleep, Scala.Toplevel, Bytes.Decode_Base64, Bytes.Encode_Base64, Doc.Doc_Names, Bash.Process, Bibtex.Check_Database, Isabelle_System.Make_Directory, Isabelle_System.Copy_Dir, Isabelle_System.Copy_File, Isabelle_System.Copy_File_Base, Isabelle_System.Rm_Tree, Isabelle_System.Download, Isabelle_System.Isabelle_Id, Isabelle_Tool.Isabelle_Tools, isabelle.atp.SystemOnTPTP.List_Systems, isabelle.atp.SystemOnTPTP.Run_System) diff --git a/src/Pure/Tools/jedit.ML b/src/Pure/Tools/jedit.ML --- a/src/Pure/Tools/jedit.ML +++ b/src/Pure/Tools/jedit.ML @@ -1,90 +1,89 @@ (* Title: Pure/Tools/jedit.ML Author: Makarius Support for Isabelle/jEdit. *) signature JEDIT = sig val get_actions: unit -> string list val check_action: string * Position.T -> string end; structure JEdit: JEDIT = struct (* parse XML *) fun parse_named a (XML.Elem ((b, props), _)) = (case Properties.get props "NAME" of SOME name => if a = b then [name] else [] | NONE => []) | parse_named _ _ = []; fun parse_actions (XML.Elem (("ACTIONS", _), body)) = maps (parse_named "ACTION") body | parse_actions _ = []; fun parse_dockables (XML.Elem (("DOCKABLES", _), body)) = maps (parse_named "DOCKABLE") body |> maps (fn a => [a, a ^ "-toggle", a ^ "-float"]) | parse_dockables _ = []; (* XML resources *) val xml_file = XML.parse o File.read; fun xml_resource name = let - val res = - Isabelle_System.bash_process ("unzip -p \"$JEDIT_HOME/dist/jedit.jar\" " ^ Bash.string name); + val res = Isabelle_System.bash_process ("unzip -p \"$JEDIT_JAR\" " ^ Bash.string name); val rc = Process_Result.rc res; in res |> Process_Result.check |> Process_Result.out |> XML.parse handle ERROR _ => error ("Cannot unzip jedit.jar\nreturn code = " ^ string_of_int rc) end; (* actions *) val lazy_actions = Lazy.lazy (fn () => - (parse_actions (xml_file \<^file>\~~/src/Tools/jEdit/src/actions.xml\) @ - parse_dockables (xml_file \<^file>\~~/src/Tools/jEdit/src/dockables.xml\) @ + (parse_actions (xml_file \<^file>\~~/src/Tools/jEdit/jedit_main/actions.xml\) @ + parse_dockables (xml_file \<^file>\~~/src/Tools/jEdit/jedit_main/dockables.xml\) @ parse_actions (xml_resource "org/gjt/sp/jedit/actions.xml") @ parse_dockables (xml_resource "org/gjt/sp/jedit/dockables.xml")) |> sort_strings); fun get_actions () = Lazy.force lazy_actions; fun check_action (name, pos) = if member (op =) (get_actions ()) name then let val ((bg1, bg2), en) = YXML.output_markup_elem (Active.make_markup Markup.jedit_actionN {implicit = false, properties = []}); val msg = "Invoke " ^ bg1 ^ name ^ bg2 ^ name ^ en ^ " jEdit action"; in writeln (msg ^ Position.here pos); name end else let val completion_report = Completion.make_report (name, pos) (fn completed => get_actions () |> filter completed |> sort_strings |> map (fn a => (a, ("action", a)))); in error ("Bad jEdit action " ^ quote name ^ Position.here pos ^ completion_report) end; val _ = Theory.setup (Document_Output.antiquotation_verbatim_embedded \<^binding>\action\ (Scan.lift Args.embedded_position) (fn ctxt => fn (name, pos) => let val _ = if Context_Position.is_reported ctxt pos then ignore (check_action (name, pos)) else (); in name end)); end; diff --git a/src/Pure/Tools/scala_project.scala b/src/Pure/Tools/scala_project.scala --- a/src/Pure/Tools/scala_project.scala +++ b/src/Pure/Tools/scala_project.scala @@ -1,190 +1,185 @@ /* Title: Pure/Tools/scala_project.scala Author: Makarius Setup Gradle project for Isabelle/Scala/jEdit. */ package isabelle object Scala_Project { /* groovy syntax */ def groovy_string(s: String): String = { s.map(c => c match { case '\t' | '\b' | '\n' | '\r' | '\f' | '\\' | '\'' | '"' => "\\" + c case _ => c.toString }).mkString("'", "", "'") } /* file and directories */ lazy val isabelle_files: List[String] = { val files1 = { val isabelle_home = Path.ISABELLE_HOME.canonical Path.split(Isabelle_System.getenv("ISABELLE_CLASSPATH")). map(path => File.relative_path(isabelle_home, path).getOrElse(path).implode) } + val isabelle_jar = Path.explode("$ISABELLE_SCALA_JAR").java_path + val isabelle_shasum = isabelle.setup.Build.get_shasum(isabelle_jar) + val files2 = - (for { - path <- - List( - Path.explode("~~/lib/classes/Pure.shasum"), - Path.explode("~~/src/Tools/jEdit/dist/Isabelle-jEdit.shasum")) - if path.is_file - line <- Library.trim_split_lines(File.read(path)) + for { + line <- Library.trim_split_lines(isabelle_shasum) name = - if (line.length > 42 && line(41) == '*') line.substring(42) + if (line.length > 41 && line(40) == ' ') line.substring(41) else error("Bad shasum entry: " + quote(line)) - if name != "lib/classes/Pure.jar" && - name != "src/Tools/jEdit/dist/jedit.jar" && - name != "src/Tools/jEdit/dist/jars/Isabelle-jEdit-base.jar" && - name != "src/Tools/jEdit/dist/jars/Isabelle-jEdit.jar" - } yield name) + if Path.is_wellformed(name) && name != "" + } yield name files1 ::: files2 } lazy val isabelle_scala_files: Map[String, Path] = isabelle_files.foldLeft(Map.empty[String, Path]) { case (map, name) => if (!name.startsWith("src/Tools/jEdit/") && name.endsWith(".scala")) { val path = Path.explode("~~/" + name) val base = path.base.implode map.get(base) match { case None => map + (base -> path) case Some(path1) => error("Conflicting base names: " + path + " vs. " + path1) } } else map } private def guess_package(path: Path): String = { val lines = split_lines(File.read(path)) val Package = """\bpackage\b +(?:object +)?\b((?:\w|\.)+)\b""".r lines.collectFirst({ case Package(name) => name }) getOrElse error("Failed to guess package from " + path) } /* compile-time position */ def here: Here = { val exn = new Exception exn.getStackTrace.toList match { case _ :: caller :: _ => val name = proper_string(caller.getFileName).getOrElse("") val line = caller.getLineNumber new Here(name, line) case _ => new Here("", 0) } } class Here private[Scala_Project](name: String, line: Int) { override def toString: String = name + ":" + line def position: Position.T = isabelle_scala_files.get(name) match { case Some(path) => Position.Line_File(line, path.implode) case None => Position.none } } /* scala project */ def scala_project(project_dir: Path, symlinks: Boolean = false): Unit = { if (symlinks && Platform.is_windows) error("Cannot create symlinks on Windows") if (project_dir.is_file || project_dir.is_dir) error("Project directory already exists: " + project_dir) val java_src_dir = project_dir + Path.explode("src/main/java") val scala_src_dir = Isabelle_System.make_directory(project_dir + Path.explode("src/main/scala")) - Isabelle_System.copy_dir(Path.explode("~~/src/Tools/jEdit/dist/jEdit"), java_src_dir) + Isabelle_System.copy_dir(Path.explode("$JEDIT_HOME/jEdit"), java_src_dir) val isabelle_setup_dir = Path.explode("~~/src/Tools/Setup/isabelle") if (symlinks) Isabelle_System.symlink(isabelle_setup_dir, java_src_dir) else Isabelle_System.copy_dir(isabelle_setup_dir, java_src_dir) val files = isabelle_files isabelle_scala_files for (file <- files if file.endsWith(".scala")) { val path = Path.ISABELLE_HOME + Path.explode(file) val target = scala_src_dir + Path.basic(guess_package(path)) Isabelle_System.make_directory(target) if (symlinks) Isabelle_System.symlink(path, target) else Isabelle_System.copy_file(path, target) } val jars = for (file <- files if file.endsWith(".jar")) yield { if (file.startsWith("/")) file else Isabelle_System.getenv("ISABELLE_HOME") + "/" + file } File.write(project_dir + Path.explode("settings.gradle"), "rootProject.name = 'Isabelle'\n") File.write(project_dir + Path.explode("build.gradle"), """plugins { id 'scala' } repositories { mavenCentral() } dependencies { implementation 'org.scala-lang:scala-library:""" + scala.util.Properties.versionNumberString + """' compile files( """ + jars.map(jar => groovy_string(File.platform_path(jar))).mkString("", ",\n ", ")") + """ } """) } /* Isabelle tool wrapper */ val isabelle_tool = Isabelle_Tool("scala_project", "setup Gradle project for Isabelle/Scala/jEdit", Scala_Project.here, args => { var symlinks = false val getopts = Getopts(""" Usage: isabelle scala_project [OPTIONS] PROJECT_DIR Options are: -L make symlinks to original scala files Setup Gradle project for Isabelle/Scala/jEdit --- to support Scala IDEs such as IntelliJ IDEA. """, "L" -> (_ => symlinks = true)) val more_args = getopts(args) val project_dir = more_args match { case List(dir) => Path.explode(dir) case _ => getopts.usage() } scala_project(project_dir, symlinks = symlinks) }) } diff --git a/src/Pure/build-jars b/src/Pure/build-jars deleted file mode 100755 --- a/src/Pure/build-jars +++ /dev/null @@ -1,328 +0,0 @@ -#!/usr/bin/env bash -# -# Author: Makarius -# -# build-jars - build Isabelle/Scala -# -# Requires proper Isabelle settings environment. - -## sources - -declare -a SOURCES=( - src/HOL/SPARK/Tools/spark.scala - src/HOL/Tools/ATP/system_on_tptp.scala - src/HOL/Tools/Mirabelle/mirabelle.scala - src/HOL/Tools/Nitpick/kodkod.scala - src/Pure/Admin/afp.scala - src/Pure/Admin/build_csdp.scala - src/Pure/Admin/build_cygwin.scala - src/Pure/Admin/build_doc.scala - src/Pure/Admin/build_e.scala - src/Pure/Admin/build_fonts.scala - src/Pure/Admin/build_history.scala - src/Pure/Admin/build_jcef.scala - src/Pure/Admin/build_jdk.scala - src/Pure/Admin/build_jedit.scala - src/Pure/Admin/build_log.scala - src/Pure/Admin/build_polyml.scala - src/Pure/Admin/build_release.scala - src/Pure/Admin/build_spass.scala - src/Pure/Admin/build_sqlite.scala - src/Pure/Admin/build_status.scala - src/Pure/Admin/build_vampire.scala - src/Pure/Admin/build_verit.scala - src/Pure/Admin/build_zipperposition.scala - src/Pure/Admin/check_sources.scala - src/Pure/Admin/ci_profile.scala - src/Pure/Admin/isabelle_cronjob.scala - src/Pure/Admin/isabelle_devel.scala - src/Pure/Admin/jenkins.scala - src/Pure/Admin/other_isabelle.scala - src/Pure/Concurrent/consumer_thread.scala - src/Pure/Concurrent/counter.scala - src/Pure/Concurrent/delay.scala - src/Pure/Concurrent/event_timer.scala - src/Pure/Concurrent/future.scala - src/Pure/Concurrent/isabelle_thread.scala - src/Pure/Concurrent/mailbox.scala - src/Pure/Concurrent/par_list.scala - src/Pure/Concurrent/synchronized.scala - src/Pure/GUI/color_value.scala - src/Pure/GUI/desktop_app.scala - src/Pure/GUI/gui.scala - src/Pure/GUI/gui_thread.scala - src/Pure/GUI/popup.scala - src/Pure/GUI/wrap_panel.scala - src/Pure/General/antiquote.scala - src/Pure/General/bytes.scala - src/Pure/General/cache.scala - src/Pure/General/codepoint.scala - src/Pure/General/comment.scala - src/Pure/General/completion.scala - src/Pure/General/csv.scala - src/Pure/General/date.scala - src/Pure/General/exn.scala - src/Pure/General/file.scala - src/Pure/General/file_watcher.scala - src/Pure/General/graph.scala - src/Pure/General/graph_display.scala - src/Pure/General/graphics_file.scala - src/Pure/General/http.scala - src/Pure/General/json.scala - src/Pure/General/linear_set.scala - src/Pure/General/logger.scala - src/Pure/General/long_name.scala - src/Pure/General/mailman.scala - src/Pure/General/mercurial.scala - src/Pure/General/multi_map.scala - src/Pure/General/output.scala - src/Pure/General/path.scala - src/Pure/General/position.scala - src/Pure/General/pretty.scala - src/Pure/General/properties.scala - src/Pure/General/rdf.scala - src/Pure/General/scan.scala - src/Pure/General/sha1.scala - src/Pure/General/sql.scala - src/Pure/General/ssh.scala - src/Pure/General/symbol.scala - src/Pure/General/time.scala - src/Pure/General/timing.scala - src/Pure/General/untyped.scala - src/Pure/General/url.scala - src/Pure/General/utf8.scala - src/Pure/General/uuid.scala - src/Pure/General/value.scala - src/Pure/General/word.scala - src/Pure/General/xz.scala - src/Pure/Isar/document_structure.scala - src/Pure/Isar/keyword.scala - src/Pure/Isar/line_structure.scala - src/Pure/Isar/outer_syntax.scala - src/Pure/Isar/parse.scala - src/Pure/Isar/token.scala - src/Pure/ML/ml_console.scala - src/Pure/ML/ml_lex.scala - src/Pure/ML/ml_process.scala - src/Pure/ML/ml_profiling.scala - src/Pure/ML/ml_statistics.scala - src/Pure/ML/ml_syntax.scala - src/Pure/PIDE/byte_message.scala - src/Pure/PIDE/command.scala - src/Pure/PIDE/command_span.scala - src/Pure/PIDE/document.scala - src/Pure/PIDE/document_id.scala - src/Pure/PIDE/document_status.scala - src/Pure/PIDE/editor.scala - src/Pure/PIDE/headless.scala - src/Pure/PIDE/line.scala - src/Pure/PIDE/markup.scala - src/Pure/PIDE/markup_tree.scala - src/Pure/PIDE/protocol.scala - src/Pure/PIDE/protocol_handlers.scala - src/Pure/PIDE/protocol_message.scala - src/Pure/PIDE/prover.scala - src/Pure/PIDE/query_operation.scala - src/Pure/PIDE/rendering.scala - src/Pure/PIDE/resources.scala - src/Pure/PIDE/session.scala - src/Pure/PIDE/text.scala - src/Pure/PIDE/xml.scala - src/Pure/PIDE/yxml.scala - src/Pure/ROOT.scala - src/Pure/System/bash.scala - src/Pure/System/command_line.scala - src/Pure/System/components.scala - src/Pure/System/executable.scala - src/Pure/System/getopts.scala - src/Pure/System/isabelle_charset.scala - src/Pure/System/isabelle_fonts.scala - src/Pure/System/isabelle_platform.scala - src/Pure/System/isabelle_process.scala - src/Pure/System/isabelle_system.scala - src/Pure/System/isabelle_tool.scala - src/Pure/System/java_statistics.scala - src/Pure/System/linux.scala - src/Pure/System/mingw.scala - src/Pure/System/numa.scala - src/Pure/System/options.scala - src/Pure/System/platform.scala - src/Pure/System/posix_interrupt.scala - src/Pure/System/process_result.scala - src/Pure/System/progress.scala - src/Pure/System/scala.scala - src/Pure/System/system_channel.scala - src/Pure/System/tty_loop.scala - src/Pure/Thy/bibtex.scala - src/Pure/Thy/document_build.scala - src/Pure/Thy/export.scala - src/Pure/Thy/export_theory.scala - src/Pure/Thy/file_format.scala - src/Pure/Thy/html.scala - src/Pure/Thy/latex.scala - src/Pure/Thy/presentation.scala - src/Pure/Thy/sessions.scala - src/Pure/Thy/thy_element.scala - src/Pure/Thy/thy_header.scala - src/Pure/Thy/thy_syntax.scala - src/Pure/Tools/build.scala - src/Pure/Tools/build_docker.scala - src/Pure/Tools/build_job.scala - src/Pure/Tools/check_keywords.scala - src/Pure/Tools/debugger.scala - src/Pure/Tools/doc.scala - src/Pure/Tools/dump.scala - src/Pure/Tools/fontforge.scala - src/Pure/Tools/java_monitor.scala - src/Pure/Tools/logo.scala - src/Pure/Tools/main.scala - src/Pure/Tools/mkroot.scala - src/Pure/Tools/phabricator.scala - src/Pure/Tools/print_operation.scala - src/Pure/Tools/profiling_report.scala - src/Pure/Tools/scala_project.scala - src/Pure/Tools/server.scala - src/Pure/Tools/server_commands.scala - src/Pure/Tools/simplifier_trace.scala - src/Pure/Tools/spell_checker.scala - src/Pure/Tools/task_statistics.scala - src/Pure/Tools/update.scala - src/Pure/Tools/update_cartouches.scala - src/Pure/Tools/update_comments.scala - src/Pure/Tools/update_header.scala - src/Pure/Tools/update_then.scala - src/Pure/Tools/update_theorems.scala - src/Pure/library.scala - src/Pure/pure_thy.scala - src/Pure/term.scala - src/Pure/term_xml.scala - src/Pure/thm_name.scala - src/Tools/Graphview/graph_file.scala - src/Tools/Graphview/graph_panel.scala - src/Tools/Graphview/graphview.scala - src/Tools/Graphview/layout.scala - src/Tools/Graphview/main_panel.scala - src/Tools/Graphview/metrics.scala - src/Tools/Graphview/model.scala - src/Tools/Graphview/mutator.scala - src/Tools/Graphview/mutator_dialog.scala - src/Tools/Graphview/mutator_event.scala - src/Tools/Graphview/popups.scala - src/Tools/Graphview/shapes.scala - src/Tools/Graphview/tree_panel.scala - src/Tools/VSCode/src/build_vscode.scala - src/Tools/VSCode/src/channel.scala - src/Tools/VSCode/src/dynamic_output.scala - src/Tools/VSCode/src/language_server.scala - src/Tools/VSCode/src/lsp.scala - src/Tools/VSCode/src/preview_panel.scala - src/Tools/VSCode/src/state_panel.scala - src/Tools/VSCode/src/textmate_grammar.scala - src/Tools/VSCode/src/vscode_model.scala - src/Tools/VSCode/src/vscode_rendering.scala - src/Tools/VSCode/src/vscode_resources.scala - src/Tools/VSCode/src/vscode_spell_checker.scala -) - - -## diagnostics - -PRG="$(basename "$0")" - -function usage() -{ - echo - echo "Usage: isabelle $PRG [OPTIONS]" - echo - echo " Options are:" - echo " -f fresh build" - echo - exit 1 -} - -function fail() -{ - echo "$1" >&2 - exit 2 -} - -[ -z "$ISABELLE_HOME" ] && fail "Missing Isabelle settings environment" - - -## process command line - -# options - -FRESH="" - -while getopts "f" OPT -do - case "$OPT" in - f) - FRESH=true - ;; - \?) - usage - ;; - esac -done - -shift $(($OPTIND - 1)) - - -# args - -[ "$#" -ne 0 ] && usage - - -## target - -TARGET_DIR="lib/classes" -TARGET_JAR="$TARGET_DIR/Pure.jar" -TARGET_SHASUM="$TARGET_DIR/Pure.shasum" - -function target_shasum() -{ - shasum -a1 -b "$TARGET_JAR" "${SOURCES[@]}" 2>/dev/null -} - -function target_clean() -{ - rm -rf "$TARGET_DIR" -} - -[ -n "$FRESH" ] && target_clean - - -## build - -target_shasum | cmp "$TARGET_SHASUM" >/dev/null 2>/dev/null -if [ "$?" -ne 0 ]; then - echo "### Building Isabelle/Scala ..." - - target_clean - - BUILD_DIR="$TARGET_DIR/build" - mkdir -p "$BUILD_DIR" - - ( - export CLASSPATH="$(platform_path "$ISABELLE_CLASSPATH")" - isabelle_scala scalac $ISABELLE_SCALAC_OPTIONS \ - -d "$BUILD_DIR" "${SOURCES[@]}" - ) || fail "Failed to compile sources" - - CHARSET_SERVICE="META-INF/services/java.nio.charset.spi.CharsetProvider" - mkdir -p "$BUILD_DIR/$(dirname "$CHARSET_SERVICE")" - echo isabelle.Isabelle_Charset_Provider > "$BUILD_DIR/$CHARSET_SERVICE" - - cp "$ISABELLE_HOME/lib/logo/isabelle_transparent-32.gif" "$BUILD_DIR/isabelle/." - cp "$ISABELLE_HOME/lib/logo/isabelle_transparent.gif" "$BUILD_DIR/isabelle/." - - isabelle_jdk jar -c -f "$(platform_path "$TARGET_JAR")" -e isabelle.Main \ - -C "$BUILD_DIR" META-INF \ - -C "$BUILD_DIR" isabelle || fail "Failed to produce $TARGET_JAR" - - rm -rf "$BUILD_DIR" - - target_shasum > "$TARGET_SHASUM" -fi diff --git a/src/Tools/Setup/isabelle/setup/Build.java b/src/Tools/Setup/isabelle/setup/Build.java --- a/src/Tools/Setup/isabelle/setup/Build.java +++ b/src/Tools/Setup/isabelle/setup/Build.java @@ -1,507 +1,498 @@ /* Title: Tools/Setup/isabelle/setup/Build.java Author: Makarius Build Isabelle/Scala/Java modules. */ package isabelle.setup; import java.io.BufferedOutputStream; import java.io.File; import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.TreeMap; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.stream.Stream; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import scala.tools.nsc.MainClass; public class Build { /** context **/ public static String BUILD_PROPS = "build.props"; public static String COMPONENT_BUILD_PROPS = "etc/build.props"; public static Context directory_context(Path dir) throws IOException { Properties props = new Properties(); props.load(Files.newBufferedReader(dir.resolve(BUILD_PROPS))); return new Context(dir, props); } public static Context component_context(Path dir) throws IOException { Properties props = new Properties(); Path build_props = dir.resolve(COMPONENT_BUILD_PROPS); if (Files.exists(build_props)) { props.load(Files.newBufferedReader(build_props)); } return new Context(dir, props); } public static List component_contexts() throws IOException, InterruptedException { List result = new LinkedList(); for (String p : Environment.getenv("ISABELLE_COMPONENTS").split(":", -1)) { if (!p.isEmpty()) { Path dir = Path.of(Environment.platform_path(p)); if (Files.exists(dir.resolve(COMPONENT_BUILD_PROPS))) { result.add(component_context(dir)); } } } return List.copyOf(result); } private static String sha_digest(MessageDigest sha, String name) { String digest = String.format(Locale.ROOT, "%040x", new BigInteger(1, sha.digest())); return digest + " " + name + "\n"; } public static class Context { private final Path _dir; private final Properties _props; public Context(Path dir, Properties props) { _dir = dir; _props = props; } @Override public String toString() { return _dir.toString(); } public String name() { return _props.getProperty("name", _dir.toFile().getName()); } public String description() { return _props.getProperty("description", name()); } public String lib_name() { return _props.getProperty("lib", "lib") + "/" + name(); } public String jar_name() { return lib_name() + ".jar"; } public String scalac_options() { return _props.getProperty("scalac_options", ""); } public String javac_options() { return _props.getProperty("javac_options", ""); } public String main() { return _props.getProperty("main", ""); } private List get_list(String name) { List list = new LinkedList(); for (String s : _props.getProperty(name, "").split("\\s+")) { if (!s.isEmpty()) { list.add(s); } } return List.copyOf(list); } public List requirements() { return get_list("requirements"); } public List sources() { return get_list("sources"); } public List resources() { return get_list("resources"); } public List services() { return get_list("services"); } public boolean is_vacuous() { return sources().isEmpty() && resources().isEmpty() && services().isEmpty(); } public Path path(String file) throws IOException, InterruptedException { return _dir.resolve(Environment.expand_platform_path(file)); } public boolean exists(String file) throws IOException, InterruptedException { return Files.exists(path(file)); } - // historic - public Path shasum_path() - throws IOException, InterruptedException - { - return path(lib_name() + ".shasum"); - } - public String item_name(String s) { int i = s.indexOf(':'); return i > 0 ? s.substring(0, i) : s; } public String item_target(String s) { int i = s.indexOf(':'); return i > 0 ? s.substring(i + 1) : s; } public String shasum(String name, List paths) throws IOException, NoSuchAlgorithmException { MessageDigest sha = MessageDigest.getInstance("SHA"); for (Path file : paths) { if (Files.exists(file)) { sha.update(Files.readAllBytes(file)); } else { throw new RuntimeException( "Missing input file " + Environment.quote(file.toString())); } } return sha_digest(sha, name); } public String shasum(String file) throws IOException, NoSuchAlgorithmException, InterruptedException { return shasum(file, List.of(path(file))); } public String shasum_props() throws NoSuchAlgorithmException { TreeMap sorted = new TreeMap(); for (Object x : _props.entrySet()) { sorted.put(x.toString(), _props.get(x)); } MessageDigest sha = MessageDigest.getInstance("SHA"); sha.update(sorted.toString().getBytes(StandardCharsets.UTF_8)); return sha_digest(sha, ""); } } /** compile sources **/ private static void add_options(List options_list, String options) { if (options != null) { for (String s : options.split("\\s+")) { if (!s.isEmpty()) { options_list.add(s); } } } } public static void compile_scala_sources( Path target_dir, String more_options, List deps, List sources) throws IOException, InterruptedException { ArrayList args = new ArrayList(); add_options(args, Environment.getenv("ISABELLE_SCALAC_OPTIONS")); add_options(args, more_options); args.add("-d"); args.add(target_dir.toString()); args.add("-bootclasspath"); args.add(Environment.join_platform_paths(deps)); args.add("--"); boolean scala_sources = false; for (Path p : sources) { args.add(p.toString()); if (p.toString().endsWith(".scala")) { scala_sources = true; } } if (scala_sources) { MainClass main = new MainClass(); boolean ok = main.process(args.toArray(String[]::new)); if (!ok) throw new RuntimeException("Failed to compile Scala sources"); } } public static void compile_java_sources( Path target_dir, String more_options, List deps, List sources) throws IOException, InterruptedException { JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager file_manager = compiler.getStandardFileManager(null, Locale.ROOT, StandardCharsets.UTF_8); List options = new LinkedList(); add_options(options, Environment.getenv("ISABELLE_JAVAC_OPTIONS")); add_options(options, more_options); options.add("-d"); options.add(target_dir.toString()); options.add("-classpath"); options.add(Environment.join_platform_paths(deps)); List java_sources = new LinkedList(); for (Path p : sources) { if (p.toString().endsWith(".java")) { for (JavaFileObject o : file_manager.getJavaFileObjectsFromPaths(List.of(p))) { java_sources.add(o); } } } if (!java_sources.isEmpty()) { boolean ok = compiler.getTask(null, file_manager, null, options, null, java_sources).call(); if (!ok) throw new RuntimeException("Failed to compile Java sources"); } } /** shasum for jar content **/ private static String SHASUM = "META-INF/isabelle/shasum"; public static String get_shasum(Path jar_path) throws IOException { if (Files.exists(jar_path)) { try (JarFile jar_file = new JarFile(jar_path.toFile())) { JarEntry entry = jar_file.getJarEntry(SHASUM); if (entry != null) { byte[] bytes = jar_file.getInputStream(entry).readAllBytes(); return new String(bytes, StandardCharsets.UTF_8); } else { return ""; } } } else { return ""; } } public static void create_shasum(Path dir, String shasum) throws IOException { Path path = dir.resolve(SHASUM); Files.createDirectories(path.getParent()); Files.writeString(path, shasum); } /** services **/ private static String SERVICES = "META-INF/isabelle/services"; public static List get_services(Path jar_path) throws IOException { if (Files.exists(jar_path)) { try (JarFile jar_file = new JarFile(jar_path.toFile())) { JarEntry entry = jar_file.getJarEntry(SERVICES); if (entry != null) { byte[] bytes = jar_file.getInputStream(entry).readAllBytes(); return Library.split_lines(new String(bytes, StandardCharsets.UTF_8)); } else { return List.of(); } } } else { return List.of(); } } public static void create_services(Path dir, List services) throws IOException { if (!services.isEmpty()) { Path path = dir.resolve(SERVICES); Files.createDirectories(path.getParent()); Files.writeString(path, Library.cat_lines(services)); } } /** create jar **/ public static void create_jar(Path dir, String main, Path jar) throws IOException { Files.createDirectories(dir.resolve(jar).getParent()); Files.deleteIfExists(jar); Manifest manifest = new Manifest(); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); attributes.put(new Attributes.Name("Created-By"), System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + ")"); if (!main.isEmpty()) { attributes.put(Attributes.Name.MAIN_CLASS, main); } try (JarOutputStream out = new JarOutputStream(new BufferedOutputStream(Files.newOutputStream(jar)), manifest)) { for (Path path : Files.walk(dir).sorted().toArray(Path[]::new)) { boolean is_dir = Files.isDirectory(path); boolean is_file = Files.isRegularFile(path); if (is_dir || is_file) { String name = Environment.slashes(dir.relativize(path).toString()); if (!name.isEmpty()) { JarEntry entry = new JarEntry(is_dir ? name + "/" : name); entry.setTime(path.toFile().lastModified()); out.putNextEntry(entry); if (is_file) { out.write(Files.readAllBytes(path)); } out.closeEntry(); } } } } } /** classpath **/ public static List classpath() throws IOException, InterruptedException { List result = new LinkedList(); for (Context context : component_contexts()) { result.add(context.path(context.jar_name())); } return List.copyOf(result); } public static List services() throws IOException, InterruptedException { List result = new LinkedList(); for (Context context : component_contexts()) { for (String s : context.services()) { result.add(s); } } return List.copyOf(result); } /** build **/ public static void build(Context context, boolean fresh) throws IOException, InterruptedException, NoSuchAlgorithmException { String jar_name = context.jar_name(); Path jar_path = context.path(jar_name); List requirements = context.requirements(); List resources = context.resources(); List sources = context.sources(); - Files.deleteIfExists(context.shasum_path()); - if (context.is_vacuous()) { Files.deleteIfExists(jar_path); } else { String shasum_old = get_shasum(jar_path); String shasum; List compiler_deps = new LinkedList(); { StringBuilder _shasum = new StringBuilder(); _shasum.append(context.shasum_props()); for (String s : requirements) { if (s.startsWith("env:")) { List paths = new LinkedList(); for (String p : Environment.getenv(s.substring(4)).split(":", -1)) { if (!p.isEmpty()) { Path path = Path.of(Environment.platform_path(p)); compiler_deps.add(path); paths.add(path); } } _shasum.append(context.shasum(s, paths)); } else { compiler_deps.add(context.path(s)); _shasum.append(context.shasum(s)); } } for (String s : resources) { _shasum.append(context.shasum(context.item_name(s))); } for (String s : sources) { _shasum.append(context.shasum(s)); } shasum = _shasum.toString(); } if (fresh || !shasum_old.equals(shasum)) { System.out.print( "### Building " + context.description() + " (" + jar_path + ") ...\n"); String isabelle_class_path = Environment.getenv("ISABELLE_CLASSPATH"); Path build_dir = Files.createTempDirectory("isabelle"); try { /* compile sources */ for (String s : isabelle_class_path.split(":", -1)) { if (!s.isEmpty()) { compiler_deps.add(Path.of(Environment.platform_path(s))); } } List compiler_sources = new LinkedList(); for (String s : sources) { compiler_sources.add(context.path(s)); } compile_scala_sources( build_dir, context.scalac_options(), compiler_deps, compiler_sources); compiler_deps.add(build_dir); compile_java_sources( build_dir, context.javac_options(), compiler_deps, compiler_sources); /* copy resources */ for (String s : context.resources()) { String name = context.item_name(s); String target = context.item_target(s); Path file_name = Path.of(name).normalize().getFileName(); Path target_path = Path.of(target).normalize(); Path target_dir; Path target_file; { if (target.endsWith("/") || target.endsWith("/.")) { target_dir = build_dir.resolve(target_path); target_file = target_dir.resolve(file_name); } else { target_file = build_dir.resolve(target_path); target_dir = target_file.getParent(); } } Files.createDirectories(target_dir); Files.copy(context.path(name), target_file, StandardCopyOption.COPY_ATTRIBUTES); } /* packaging */ create_shasum(build_dir, shasum); create_services(build_dir, context.services()); create_jar(build_dir, context.main(), jar_path); } finally { try (Stream walk = Files.walk(build_dir)) { walk.sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } } } } } public static void build_components(boolean fresh) throws IOException, InterruptedException, NoSuchAlgorithmException { for (Context context : component_contexts()) { build(context, fresh); } } } diff --git a/src/Tools/jEdit/README b/src/Tools/jEdit/README --- a/src/Tools/jEdit/README +++ b/src/Tools/jEdit/README @@ -1,12 +1,3 @@ -These are the main sources of Isabelle/jEdit, which is a plugin for -the jEdit text-editor, with some minor modifications according to -patches/. - -Original jEdit is available from http://www.jedit.org -- it is -licensed according to GPL, and the derivative version produced in -directory "dist" inherits that. - -Note that Isabelle repository versions refer to a contributed -component called jedit_build-JJJJMMDD, which also includes the full -sources after applying the patches, together with further add-on -modules. +These are the sources of Isabelle/jEdit. The main jEdit text-editor is +provided as a separate component: it includes the full sources after +applying some minor patches, together with further add-on modules. diff --git a/src/Tools/jEdit/etc/settings b/src/Tools/jEdit/etc/settings --- a/src/Tools/jEdit/etc/settings +++ b/src/Tools/jEdit/etc/settings @@ -1,14 +1,4 @@ # -*- shell-script -*- :mode=shellscript: -JEDIT_HOME="$COMPONENT" -JEDIT_SETTINGS="$ISABELLE_HOME_USER/jedit" - -JEDIT_OPTIONS="-reuseview -nobackground -nosplash -log=9" - -JEDIT_JAVA_OPTIONS="-Xms512m -Xmx4g -Xss16m" -JEDIT_JAVA_SYSTEM_OPTIONS="-Duser.language=en -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true -Dapple.laf.useScreenMenuBar=true -Dapple.awt.application.name=Isabelle" - ISABELLE_JEDIT_OPTIONS="" - -ISABELLE_TOOLS="$ISABELLE_TOOLS:$JEDIT_HOME/lib/Tools" -ISABELLE_DOCS="$ISABELLE_DOCS:$JEDIT_HOME/dist/doc" +ISABELLE_TOOLS="$ISABELLE_TOOLS:$COMPONENT/lib/Tools" diff --git a/src/Tools/jEdit/jedit_base/build.props b/src/Tools/jEdit/jedit_base/build.props new file mode 100644 --- /dev/null +++ b/src/Tools/jEdit/jedit_base/build.props @@ -0,0 +1,11 @@ +description = Isabelle/jEdit base plugin +lib = $JEDIT_SETTINGS/jars +name = isabelle_jedit_base +requirements = \ + env:ISABELLE_SCALA_JAR \ + env:JEDIT_JARS +resources = \ + plugin.props \ + services.xml +sources = \ + plugin.scala diff --git a/src/Tools/jEdit/src-base/Isabelle_Base.props b/src/Tools/jEdit/jedit_base/plugin.props rename from src/Tools/jEdit/src-base/Isabelle_Base.props rename to src/Tools/jEdit/jedit_base/plugin.props --- a/src/Tools/jEdit/src-base/Isabelle_Base.props +++ b/src/Tools/jEdit/jedit_base/plugin.props @@ -1,17 +1,17 @@ -## Isabelle_Base plugin properties +## Isabelle/jEdit plugin properties ## ##:encoding=ISO-8859-1:wrap=soft:maxLineLen=100: #identification plugin.isabelle.jedit_base.Plugin.name=Isabelle Base plugin.isabelle.jedit_base.Plugin.author=Makarius Wenzel plugin.isabelle.jedit_base.Plugin.version=1.0 plugin.isabelle.jedit_base.Plugin.description=Isabelle Base: DO NOT UNLOAD! #system parameters plugin.isabelle.jedit_base.Plugin.activate=startup plugin.isabelle.jedit_base.Plugin.usePluginHome=false #dependencies plugin.isabelle.jedit_base.Plugin.depend.0=jdk 11 plugin.isabelle.jedit_base.Plugin.depend.1=jedit 05.05.00.00 diff --git a/src/Tools/jEdit/jedit_base/plugin.scala b/src/Tools/jEdit/jedit_base/plugin.scala new file mode 100644 --- /dev/null +++ b/src/Tools/jEdit/jedit_base/plugin.scala @@ -0,0 +1,9 @@ +/* Title: Tools/jEdit/jedit_base/plugin.scala + Author: Makarius + +Isabelle/jEdit base plugin. +*/ + +package isabelle.jedit_base + +class Plugin extends isabelle.jedit.Base_Plugin diff --git a/src/Tools/jEdit/src-base/services.xml b/src/Tools/jEdit/jedit_base/services.xml rename from src/Tools/jEdit/src-base/services.xml rename to src/Tools/jEdit/jedit_base/services.xml --- a/src/Tools/jEdit/src-base/services.xml +++ b/src/Tools/jEdit/jedit_base/services.xml @@ -1,11 +1,11 @@ - new isabelle.jedit_base.Isabelle_Encoding(); + new isabelle.jedit.Isabelle_Encoding(); - new isabelle.jedit_base.PIDE_Docking_Framework(); + new isabelle.jedit.PIDE_Docking_Framework(); diff --git a/src/Tools/jEdit/src/actions.xml b/src/Tools/jEdit/jedit_main/actions.xml rename from src/Tools/jEdit/src/actions.xml rename to src/Tools/jEdit/jedit_main/actions.xml diff --git a/src/Tools/jEdit/jedit_main/build.props b/src/Tools/jEdit/jedit_main/build.props new file mode 100644 --- /dev/null +++ b/src/Tools/jEdit/jedit_main/build.props @@ -0,0 +1,15 @@ +description = Isabelle/jEdit main plugin +lib = $JEDIT_SETTINGS/jars +name = isabelle_jedit_main +requirements = \ + env:ISABELLE_SCALA_JAR \ + env:JEDIT_JARS +resources = \ + actions.xml \ + dockables.xml \ + plugin.props \ + services.xml +sources = \ + isabelle_sidekick.scala \ + plugin.scala \ + scala_console.scala diff --git a/src/Tools/jEdit/src/dockables.xml b/src/Tools/jEdit/jedit_main/dockables.xml rename from src/Tools/jEdit/src/dockables.xml rename to src/Tools/jEdit/jedit_main/dockables.xml diff --git a/src/Tools/jEdit/src/isabelle_sidekick.scala b/src/Tools/jEdit/jedit_main/isabelle_sidekick.scala rename from src/Tools/jEdit/src/isabelle_sidekick.scala rename to src/Tools/jEdit/jedit_main/isabelle_sidekick.scala --- a/src/Tools/jEdit/src/isabelle_sidekick.scala +++ b/src/Tools/jEdit/jedit_main/isabelle_sidekick.scala @@ -1,268 +1,305 @@ /* Title: Tools/jEdit/src/isabelle_sidekick.scala Author: Fabian Immler, TU Munich Author: Makarius SideKick parsers for Isabelle proof documents. */ -package isabelle.jedit +package isabelle.jedit_main import isabelle._ +import isabelle.jedit._ import javax.swing.tree.DefaultMutableTreeNode import javax.swing.text.Position import javax.swing.{JLabel, Icon} import org.gjt.sp.jedit.Buffer import sidekick.{SideKickParser, SideKickParsedData, IAsset} object Isabelle_Sidekick { def int_to_pos(offset: Text.Offset): Position = new Position { def getOffset = offset; override def toString: String = offset.toString } def root_data(buffer: Buffer): SideKickParsedData = { val data = new SideKickParsedData(buffer.getName) data.getAsset(data.root).setEnd(int_to_pos(buffer.getLength)) data } class Keyword_Asset(keyword: String, text: String, range: Text.Range) extends IAsset { private val css = GUI.imitate_font_css(GUI.label_font()) protected var _name = text protected var _start = int_to_pos(range.start) protected var _end = int_to_pos(range.stop) override def getIcon: Icon = null override def getShortString: String = { val n = keyword.length val s = _name.indexOf(keyword) match { case i if i >= 0 && n > 0 => HTML.output(_name.substring(0, i)) + "" + HTML.output(keyword) + "" + HTML.output(_name.substring(i + n)) case _ => HTML.output(_name) } "" + s + "" } override def getLongString: String = _name override def getName: String = _name override def setName(name: String): Unit = _name = name override def getStart: Position = _start override def setStart(start: Position): Unit = _start = start override def getEnd: Position = _end override def setEnd(end: Position): Unit = _end = end override def toString: String = _name } class Asset(name: String, range: Text.Range) extends Keyword_Asset("", name, range) def swing_markup_tree(tree: Markup_Tree, parent: DefaultMutableTreeNode, swing_node: Text.Info[List[XML.Elem]] => DefaultMutableTreeNode): Unit = { for ((_, entry) <- tree.branches) { val node = swing_node(Text.Info(entry.range, entry.markup)) swing_markup_tree(entry.subtree, node, swing_node) parent.add(node) } } } class Isabelle_Sidekick(name: String) extends SideKickParser(name) { override def supportsCompletion = false /* parsing */ @volatile protected var stopped = false override def stop() = { stopped = true } def parser(buffer: Buffer, syntax: Outer_Syntax, data: SideKickParsedData): Boolean = false def parse(buffer: Buffer, error_source: errorlist.DefaultErrorSource): SideKickParsedData = { stopped = false // FIXME lock buffer (!??) val data = Isabelle_Sidekick.root_data(buffer) val syntax = Isabelle.buffer_syntax(buffer) val ok = if (syntax.isDefined) { val ok = parser(buffer, syntax.get, data) if (stopped) { data.root.add(new DefaultMutableTreeNode("")); true } else ok } else false if (!ok) data.root.add(new DefaultMutableTreeNode("")) data } } class Isabelle_Sidekick_Structure( name: String, node_name: Buffer => Option[Document.Node.Name], parse: (Outer_Syntax, Document.Node.Name, CharSequence) => List[Document_Structure.Document]) extends Isabelle_Sidekick(name) { override def parser(buffer: Buffer, syntax: Outer_Syntax, data: SideKickParsedData): Boolean = { def make_tree( parent: DefaultMutableTreeNode, offset: Text.Offset, documents: List[Document_Structure.Document]): Unit = { documents.foldLeft(offset) { case (i, document) => document match { case Document_Structure.Block(name, text, body) => val range = Text.Range(i, i + document.length) val node = new DefaultMutableTreeNode( new Isabelle_Sidekick.Keyword_Asset(name, Library.first_line(text), range)) parent.add(node) make_tree(node, i, body) case _ => } i + document.length } } node_name(buffer) match { case Some(name) => make_tree(data.root, 0, parse(syntax, name, JEdit_Lib.buffer_text(buffer))) true case None => false } } } class Isabelle_Sidekick_Default extends Isabelle_Sidekick_Structure("isabelle", PIDE.resources.theory_node_name, Document_Structure.parse_sections) class Isabelle_Sidekick_Context extends Isabelle_Sidekick_Structure("isabelle-context", PIDE.resources.theory_node_name, Document_Structure.parse_blocks) class Isabelle_Sidekick_Options extends Isabelle_Sidekick_Structure("isabelle-options", _ => Some(Document.Node.Name("options")), Document_Structure.parse_sections) class Isabelle_Sidekick_Root extends Isabelle_Sidekick_Structure("isabelle-root", _ => Some(Document.Node.Name("ROOT")), Document_Structure.parse_sections) class Isabelle_Sidekick_ML extends Isabelle_Sidekick_Structure("isabelle-ml", buffer => Some(PIDE.resources.node_name(buffer)), (_, _, text) => Document_Structure.parse_ml_sections(false, text)) class Isabelle_Sidekick_SML extends Isabelle_Sidekick_Structure("isabelle-sml", buffer => Some(PIDE.resources.node_name(buffer)), (_, _, text) => Document_Structure.parse_ml_sections(true, text)) class Isabelle_Sidekick_Markup extends Isabelle_Sidekick("isabelle-markup") { override def parser(buffer: Buffer, syntax: Outer_Syntax, data: SideKickParsedData): Boolean = { val opt_snapshot = Document_Model.get(buffer) match { case Some(model) if model.is_theory => Some(model.snapshot()) case _ => None } opt_snapshot match { case Some(snapshot) => for ((command, command_start) <- snapshot.node.command_iterator() if !stopped) { val markup = snapshot.state.command_markup( snapshot.version, command, Command.Markup_Index.markup, command.range, Markup.Elements.full) Isabelle_Sidekick.swing_markup_tree(markup, data.root, (info: Text.Info[List[XML.Elem]]) => { val range = info.range + command_start val content = command.source(info.range).replace('\n', ' ') val info_text = Pretty.formatted(Pretty.fbreaks(info.info), margin = 40.0).mkString new DefaultMutableTreeNode( new Isabelle_Sidekick.Asset(command.toString, range) { override def getShortString: String = content override def getLongString: String = info_text override def toString: String = quote(content) + " " + range.toString }) }) } true case None => false } } } class Isabelle_Sidekick_News extends Isabelle_Sidekick("isabelle-news") { private val Heading1 = """^New in (.*)\w*$""".r private val Heading2 = """^\*\*\*\w*(.*)\w*\*\*\*\w*$""".r private def make_node(s: String, start: Text.Offset, stop: Text.Offset): DefaultMutableTreeNode = new DefaultMutableTreeNode(new Isabelle_Sidekick.Asset(s, Text.Range(start, stop))) override def parser(buffer: Buffer, syntax: Outer_Syntax, data: SideKickParsedData): Boolean = { var offset = 0 var end_offset = 0 var start1: Option[(Int, String, Vector[DefaultMutableTreeNode])] = None var start2: Option[(Int, String)] = None def close1: Unit = start1 match { case Some((start_offset, s, body)) => val node = make_node(s, start_offset, end_offset) body.foreach(node.add(_)) data.root.add(node) start1 = None case None => } def close2: Unit = start2 match { case Some((start_offset, s)) => start1 match { case Some((start_offset1, s1, body)) => val node = make_node(s, start_offset, end_offset) start1 = Some((start_offset1, s1, body :+ node)) case None => } start2 = None case None => } for (line <- split_lines(JEdit_Lib.buffer_text(buffer)) if !stopped) { line match { case Heading1(s) => close2; close1; start1 = Some((offset, s, Vector())) case Heading2(s) => close2; start2 = Some((offset, s)) case _ => } offset += line.length + 1 if (!line.forall(Character.isSpaceChar)) end_offset = offset } if (!stopped) { close2; close1 } true } } +class Isabelle_Sidekick_Bibtex extends SideKickParser("bibtex") +{ + override def supportsCompletion = false + + private class Asset(label: String, label_html: String, range: Text.Range, source: String) + extends Isabelle_Sidekick.Asset(label, range) { + override def getShortString: String = label_html + override def getLongString: String = source + } + + def parse(buffer: Buffer, error_source: errorlist.DefaultErrorSource): SideKickParsedData = + { + val data = Isabelle_Sidekick.root_data(buffer) + + try { + var offset = 0 + for (chunk <- Bibtex.parse(JEdit_Lib.buffer_text(buffer))) { + val kind = chunk.kind + val name = chunk.name + val source = chunk.source + if (kind != "") { + val label = kind + (if (name == "") "" else " " + name) + val label_html = + "" + HTML.output(kind) + "" + + (if (name == "") "" else " " + HTML.output(name)) + "" + val range = Text.Range(offset, offset + source.length) + val asset = new Asset(label, label_html, range, source) + data.root.add(new DefaultMutableTreeNode(asset)) + } + offset += source.length + } + data + } + catch { case ERROR(msg) => Output.warning(msg); null } + } +} diff --git a/src/Tools/jEdit/src/Isabelle.props b/src/Tools/jEdit/jedit_main/plugin.props rename from src/Tools/jEdit/src/Isabelle.props rename to src/Tools/jEdit/jedit_main/plugin.props --- a/src/Tools/jEdit/src/Isabelle.props +++ b/src/Tools/jEdit/jedit_main/plugin.props @@ -1,111 +1,111 @@ -## Isabelle plugin properties +## Isabelle/jEdit plugin properties ## ##:encoding=ISO-8859-1:wrap=soft:maxLineLen=100: #identification -plugin.isabelle.jedit.Plugin.name=Isabelle -plugin.isabelle.jedit.Plugin.author=Johannes Hölzl, Lars Hupel, Fabian Immler, Markus Kaiser, Makarius Wenzel -plugin.isabelle.jedit.Plugin.version=11.2 -plugin.isabelle.jedit.Plugin.description=Isabelle Prover IDE +plugin.isabelle.jedit_main.Plugin.name=Isabelle +plugin.isabelle.jedit_main.Plugin.author=Johannes Hölzl, Lars Hupel, Fabian Immler, Markus Kaiser, Makarius Wenzel +plugin.isabelle.jedit_main.Plugin.version=11.2 +plugin.isabelle.jedit_main.Plugin.description=Isabelle Prover IDE #system parameters -plugin.isabelle.jedit.Plugin.activate=defer -plugin.isabelle.jedit.Plugin.usePluginHome=false +plugin.isabelle.jedit_main.Plugin.activate=startup # FIXME +plugin.isabelle.jedit_main.Plugin.usePluginHome=false #dependencies -plugin.isabelle.jedit.Plugin.depend.0=jdk 11 -plugin.isabelle.jedit.Plugin.depend.1=jedit 05.05.00.00 -plugin.isabelle.jedit.Plugin.depend.2=plugin console.ConsolePlugin 5.1.4 -plugin.isabelle.jedit.Plugin.depend.3=plugin errorlist.ErrorListPlugin 2.3 -plugin.isabelle.jedit.Plugin.depend.4=plugin sidekick.SideKickPlugin 1.8 -plugin.isabelle.jedit.Plugin.depend.5=plugin isabelle.jedit_base.Plugin 1.0 +plugin.isabelle.jedit_main.Plugin.depend.0=jdk 11 +plugin.isabelle.jedit_main.Plugin.depend.1=jedit 05.05.00.00 +plugin.isabelle.jedit_main.Plugin.depend.2=plugin console.ConsolePlugin 5.1.4 +plugin.isabelle.jedit_main.Plugin.depend.3=plugin errorlist.ErrorListPlugin 2.3 +plugin.isabelle.jedit_main.Plugin.depend.4=plugin sidekick.SideKickPlugin 1.8 +plugin.isabelle.jedit_main.Plugin.depend.5=plugin isabelle.jedit_base.Plugin 1.0 #options -plugin.isabelle.jedit.Plugin.option-group=isabelle-general isabelle-rendering +plugin.isabelle.jedit_main.Plugin.option-group=isabelle-general isabelle-rendering options.isabelle-general.label=General options.isabelle-general.code=new isabelle.jedit.Isabelle_Options1(); options.isabelle-rendering.label=Rendering options.isabelle-rendering.code=new isabelle.jedit.Isabelle_Options2(); #menu actions and dockables -plugin.isabelle.jedit.Plugin.menu.label=Isabelle -plugin.isabelle.jedit.Plugin.menu= \ +plugin.isabelle.jedit_main.Plugin.menu.label=Isabelle +plugin.isabelle.jedit_main.Plugin.menu= \ isabelle-export-browser \ isabelle-session-browser \ isabelle.preview \ isabelle.draft \ isabelle.java-monitor \ - \ isabelle-debugger \ isabelle-documentation \ isabelle-monitor \ isabelle-output \ isabelle-protocol \ isabelle-query \ isabelle-raw-output \ isabelle-simplifier-trace \ isabelle-sledgehammer \ isabelle-state \ isabelle-symbols \ isabelle-syslog \ isabelle-theories \ isabelle-timing isabelle-debugger.label=Debugger panel isabelle-debugger.title=Debugger isabelle-documentation.label=Documentation panel isabelle-documentation.title=Documentation isabelle-graphview.label=Graphview panel isabelle-graphview.title=Graphview isabelle-info.label=Info panel isabelle-info.title=Info isabelle-monitor.label=Monitor panel isabelle-monitor.title=Monitor isabelle-output.label=Output panel isabelle-output.title=Output isabelle-protocol.label=Protocol panel isabelle-protocol.title=Protocol isabelle-query.label=Query panel isabelle-query.title=Query isabelle-raw-output.label=Raw Output panel isabelle-raw-output.title=Raw Output isabelle-simplifier-trace.label=Simplifier Trace panel isabelle-simplifier-trace.title=Simplifier Trace isabelle-sledgehammer.label=Sledgehammer panel isabelle-sledgehammer.title=Sledgehammer isabelle-state.label=State panel isabelle-state.title=State isabelle-symbols.label=Symbols panel isabelle-symbols.title=Symbols isabelle-syslog.label=Syslog panel isabelle-syslog.title=Syslog isabelle-theories.label=Theories panel isabelle-theories.title=Theories isabelle-timing.label=Timing panel isabelle-timing.title=Timing #SideKick mode.isabelle-news.folding=sidekick mode.isabelle-news.sidekick.parser=isabelle-news mode.isabelle-options.folding=sidekick mode.isabelle-options.sidekick.parser=isabelle-options mode.isabelle-root.folding=sidekick mode.isabelle-root.sidekick.parser=isabelle-root mode.isabelle.customSettings=true mode.isabelle.folding=isabelle mode.isabelle.sidekick.parser=isabelle mode.isabelle.sidekick.showStatusWindow.label=true mode.isabelle-ml.folding=sidekick mode.isabelle-ml.sidekick.parser=isabelle-ml mode.sml.folding=sidekick mode.sml.sidekick.parser=isabelle-sml mode.bibtex.folding=sidekick mode.bibtex.sidekick.parser=bibtex sidekick.parser.isabelle.label=isabelle sidekick.parser.isabelle-context.label=isabelle-context sidekick.parser.isabelle-markup.label=isabelle-markup sidekick.parser.isabelle-ml.label=isabelle-ml sidekick.parser.isabelle-sml.label=isabelle-sml sidekick.parser.isabelle-news.label=isabelle-news sidekick.parser.isabelle-options.label=isabelle-options sidekick.parser.isabelle-root.label=isabelle-root sidekick.parser.bibtex.label=bibtex diff --git a/src/Tools/jEdit/jedit_main/plugin.scala b/src/Tools/jEdit/jedit_main/plugin.scala new file mode 100644 --- /dev/null +++ b/src/Tools/jEdit/jedit_main/plugin.scala @@ -0,0 +1,9 @@ +/* Title: Tools/jEdit/jedit_main/plugin.scala + Author: Makarius + +Isabelle/jEdit main plugin. +*/ + +package isabelle.jedit_main + +class Plugin extends isabelle.jedit.Main_Plugin diff --git a/src/Tools/jEdit/src/scala_console.scala b/src/Tools/jEdit/jedit_main/scala_console.scala rename from src/Tools/jEdit/src/scala_console.scala rename to src/Tools/jEdit/jedit_main/scala_console.scala --- a/src/Tools/jEdit/src/scala_console.scala +++ b/src/Tools/jEdit/jedit_main/scala_console.scala @@ -1,181 +1,182 @@ -/* Title: Tools/jEdit/src/scala_console.scala +/* Title: Tools/jEdit/jedit_main/scala_console.scala Author: Makarius Scala instance of Console plugin. */ -package isabelle.jedit +package isabelle.jedit_main import isabelle._ +import isabelle.jedit._ import console.{Console, ConsolePane, Shell, Output} import org.gjt.sp.jedit.JARClassLoader import java.io.{OutputStream, Writer, PrintWriter} class Scala_Console extends Shell("Scala") { /* global state -- owned by GUI thread */ @volatile private var interpreters = Map.empty[Console, Interpreter] @volatile private var global_console: Console = null @volatile private var global_out: Output = null @volatile private var global_err: Output = null private val console_stream = new OutputStream { val buf = new StringBuilder(100) override def flush(): Unit = { val s = buf.synchronized { val s = buf.toString; buf.clear(); s } val str = UTF8.decode_permissive(s) GUI_Thread.later { if (global_out == null) System.out.print(str) else global_out.writeAttrs(null, str) } Time.seconds(0.01).sleep() // FIXME adhoc delay to avoid loosing output } override def close(): Unit = flush() def write(byte: Int): Unit = { val c = byte.toChar buf.synchronized { buf.append(c) } if (c == '\n') flush() } } private val console_writer = new Writer { def flush(): Unit = console_stream.flush() def close(): Unit = console_stream.flush() def write(cbuf: Array[Char], off: Int, len: Int): Unit = { if (len > 0) { UTF8.bytes(new String(cbuf.slice(off, off + len))).foreach(console_stream.write(_)) } } } private def with_console[A](console: Console, out: Output, err: Output)(e: => A): A = { global_console = console global_out = out global_err = if (err == null) out else err try { scala.Console.withErr(console_stream) { scala.Console.withOut(console_stream) { e } } } finally { console_stream.flush global_console = null global_out = null global_err = null } } private def report_error(str: String): Unit = { if (global_console == null || global_err == null) isabelle.Output.writeln(str) else GUI_Thread.later { global_err.print(global_console.getErrorColor, str) } } /* interpreter thread */ private abstract class Request private case class Start(console: Console) extends Request private case class Execute(console: Console, out: Output, err: Output, command: String) extends Request private class Interpreter { private val running = Synchronized[Option[Thread]](None) def interrupt(): Unit = running.change(opt => { opt.foreach(_.interrupt()); opt }) private val interp = Scala.Compiler.context(error = report_error, jar_dirs = JEdit_Lib.directories). interpreter( print_writer = new PrintWriter(console_writer, true), class_loader = new JARClassLoader) val thread: Consumer_Thread[Request] = Consumer_Thread.fork("Scala_Console") { case Start(console) => interp.bind("view", "org.gjt.sp.jedit.View", console.getView) interp.bind("console", "console.Console", console) interp.interpret("import isabelle._; import isabelle.jedit._") true case Execute(console, out, err, command) => with_console(console, out, err) { try { running.change(_ => Some(Thread.currentThread())) interp.interpret(command) } finally { running.change(_ => None) Exn.Interrupt.dispose() } GUI_Thread.later { if (err != null) err.commandDone() out.commandDone() } true } } } /* jEdit console methods */ override def openConsole(console: Console): Unit = { val interp = new Interpreter interp.thread.send(Start(console)) interpreters += (console -> interp) } override def closeConsole(console: Console): Unit = { interpreters.get(console) match { case Some(interp) => interp.interrupt() interp.thread.shutdown() interpreters -= console case None => } } override def printInfoMessage(out: Output): Unit = { out.print(null, "This shell evaluates Isabelle/Scala expressions.\n\n" + "The contents of package isabelle and isabelle.jedit are imported.\n" + "The following special toplevel bindings are provided:\n" + " view -- current jEdit/Swing view (e.g. view.getBuffer, view.getTextArea)\n" + " console -- jEdit Console plugin\n" + " PIDE -- Isabelle/PIDE plugin (e.g. PIDE.session, PIDE.snapshot, PIDE.rendering)\n") } override def printPrompt(console: Console, out: Output): Unit = { out.writeAttrs(ConsolePane.colorAttributes(console.getInfoColor), "scala>") out.writeAttrs(ConsolePane.colorAttributes(console.getPlainColor), " ") } override def execute( console: Console, input: String, out: Output, err: Output, command: String): Unit = { interpreters(console).thread.send(Execute(console, out, err, command)) } override def stop(console: Console): Unit = interpreters.get(console).foreach(_.interrupt()) } diff --git a/src/Tools/jEdit/src/services.xml b/src/Tools/jEdit/jedit_main/services.xml rename from src/Tools/jEdit/src/services.xml rename to src/Tools/jEdit/jedit_main/services.xml --- a/src/Tools/jEdit/src/services.xml +++ b/src/Tools/jEdit/jedit_main/services.xml @@ -1,59 +1,59 @@ new isabelle.jedit.Fold_Handling.Fold_Handler(); new isabelle.jedit.Context_Menu(); new isabelle.jedit.Isabelle_Export.VFS(); new isabelle.jedit.Isabelle_Session.VFS(); - new isabelle.jedit.Isabelle_Sidekick_Default(); + new isabelle.jedit_main.Isabelle_Sidekick_Default(); - new isabelle.jedit.Isabelle_Sidekick_Context(); + new isabelle.jedit_main.Isabelle_Sidekick_Context(); - new isabelle.jedit.Isabelle_Sidekick_Markup(); + new isabelle.jedit_main.Isabelle_Sidekick_Markup(); - new isabelle.jedit.Isabelle_Sidekick_ML(); + new isabelle.jedit_main.Isabelle_Sidekick_ML(); - new isabelle.jedit.Isabelle_Sidekick_SML(); + new isabelle.jedit_main.Isabelle_Sidekick_SML(); - new isabelle.jedit.Isabelle_Sidekick_News(); + new isabelle.jedit_main.Isabelle_Sidekick_News(); - new isabelle.jedit.Isabelle_Sidekick_Options(); + new isabelle.jedit_main.Isabelle_Sidekick_Options(); - new isabelle.jedit.Isabelle_Sidekick_Root(); + new isabelle.jedit_main.Isabelle_Sidekick_Root(); - new isabelle.jedit.JEdit_Bibtex.Sidekick_Parser(); + new isabelle.jedit_main.Isabelle_Sidekick_Bibtex(); - new isabelle.jedit.Scala_Console(); + new isabelle.jedit_main.Scala_Console(); new isabelle.jedit.Active$Misc_Handler(); new isabelle.jedit.Graphview_Dockable$Handler() - - new isabelle.jedit.Status_Widget$Java_Factory(); - - - new isabelle.jedit.Status_Widget$ML_Factory(); - + + new isabelle.jedit.Status_Widget$Java_Factory(); + + + new isabelle.jedit.Status_Widget$ML_Factory(); + diff --git a/src/Tools/jEdit/lib/Tools/jedit b/src/Tools/jEdit/lib/Tools/jedit --- a/src/Tools/jEdit/lib/Tools/jedit +++ b/src/Tools/jEdit/lib/Tools/jedit @@ -1,349 +1,174 @@ #!/usr/bin/env bash # # Author: Makarius # # DESCRIPTION: Isabelle/jEdit interface wrapper -## sources - -declare -a SOURCES0=( - "src/Tools/jEdit/src-base/dockable.scala" - "src/Tools/jEdit/src-base/isabelle_encoding.scala" - "src/Tools/jEdit/src-base/jedit_lib.scala" - "src/Tools/jEdit/src-base/pide_docking_framework.scala" - "src/Tools/jEdit/src-base/plugin.scala" - "src/Tools/jEdit/src-base/syntax_style.scala" -) - -declare -a RESOURCES0=( - "src/Tools/jEdit/src-base/Isabelle_Base.props" - "src/Tools/jEdit/src-base/services.xml" -) - -declare -a SOURCES=( - "src/Tools/jEdit/src/active.scala" - "src/Tools/jEdit/src/completion_popup.scala" - "src/Tools/jEdit/src/context_menu.scala" - "src/Tools/jEdit/src/debugger_dockable.scala" - "src/Tools/jEdit/src/document_model.scala" - "src/Tools/jEdit/src/document_view.scala" - "src/Tools/jEdit/src/documentation_dockable.scala" - "src/Tools/jEdit/src/fold_handling.scala" - "src/Tools/jEdit/src/font_info.scala" - "src/Tools/jEdit/src/graphview_dockable.scala" - "src/Tools/jEdit/src/info_dockable.scala" - "src/Tools/jEdit/src/isabelle.scala" - "src/Tools/jEdit/src/isabelle_encoding.scala" - "src/Tools/jEdit/src/isabelle_export.scala" - "src/Tools/jEdit/src/isabelle_options.scala" - "src/Tools/jEdit/src/isabelle_session.scala" - "src/Tools/jEdit/src/isabelle_sidekick.scala" - "src/Tools/jEdit/src/isabelle_vfs.scala" - "src/Tools/jEdit/src/jedit_bibtex.scala" - "src/Tools/jEdit/src/jedit_editor.scala" - "src/Tools/jEdit/src/jedit_lib.scala" - "src/Tools/jEdit/src/jedit_options.scala" - "src/Tools/jEdit/src/jedit_rendering.scala" - "src/Tools/jEdit/src/jedit_resources.scala" - "src/Tools/jEdit/src/jedit_sessions.scala" - "src/Tools/jEdit/src/jedit_spell_checker.scala" - "src/Tools/jEdit/src/keymap_merge.scala" - "src/Tools/jEdit/src/monitor_dockable.scala" - "src/Tools/jEdit/src/output_dockable.scala" - "src/Tools/jEdit/src/plugin.scala" - "src/Tools/jEdit/src/pretty_text_area.scala" - "src/Tools/jEdit/src/pretty_tooltip.scala" - "src/Tools/jEdit/src/process_indicator.scala" - "src/Tools/jEdit/src/protocol_dockable.scala" - "src/Tools/jEdit/src/query_dockable.scala" - "src/Tools/jEdit/src/raw_output_dockable.scala" - "src/Tools/jEdit/src/rich_text_area.scala" - "src/Tools/jEdit/src/scala_console.scala" - "src/Tools/jEdit/src/session_build.scala" - "src/Tools/jEdit/src/simplifier_trace_dockable.scala" - "src/Tools/jEdit/src/simplifier_trace_window.scala" - "src/Tools/jEdit/src/sledgehammer_dockable.scala" - "src/Tools/jEdit/src/state_dockable.scala" - "src/Tools/jEdit/src/status_widget.scala" - "src/Tools/jEdit/src/symbols_dockable.scala" - "src/Tools/jEdit/src/syntax_style.scala" - "src/Tools/jEdit/src/syslog_dockable.scala" - "src/Tools/jEdit/src/text_overview.scala" - "src/Tools/jEdit/src/text_structure.scala" - "src/Tools/jEdit/src/theories_dockable.scala" - "src/Tools/jEdit/src/timing_dockable.scala" - "src/Tools/jEdit/src/token_markup.scala" -) - -declare -a RESOURCES=( - "src/Tools/jEdit/src/actions.xml" - "src/Tools/jEdit/src/dockables.xml" - "src/Tools/jEdit/src/Isabelle.props" - "src/Tools/jEdit/src/services.xml" -) - - ## diagnostics PRG="$(basename "$0")" function usage() { echo echo "Usage: isabelle $PRG [OPTIONS] [FILES ...]" echo echo " Options are:" echo " -A NAME ancestor session for option -R (default: parent)" echo " -D NAME=X set JVM system property" echo " -J OPTION add JVM runtime option" echo " (default $JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)" echo " -R NAME build image with requirements from other sessions" echo " -b build only" echo " -d DIR include session directory" echo " -f fresh build" echo " -i NAME include session in name-space of theories" echo " -j OPTION add jEdit runtime option" echo " (default $JEDIT_OPTIONS)" echo " -l NAME logic session name" echo " -m MODE add print mode for output" echo " -n no build of session image on startup" echo " -p CMD ML process command prefix (process policy)" echo " -s system build mode for session image (system_heaps=true)" echo " -u user build mode for session image (system_heaps=false)" echo echo " Start jEdit with Isabelle plugin setup and open FILES" echo " (default \"$USER_HOME/Scratch.thy\" or \":\" for empty buffer)." echo exit 1 } function fail() { echo "$1" >&2 exit 2 } function failed() { fail "Failed!" } ## process command line # options BUILD_ONLY=false FRESH_BUILD="" ML_PROCESS_POLICY="" JEDIT_LOGIC_ANCESTOR="" JEDIT_LOGIC_REQUIREMENTS="" JEDIT_INCLUDE_SESSIONS="" JEDIT_SESSION_DIRS="-" JEDIT_LOGIC="" JEDIT_PRINT_MODE="" JEDIT_NO_BUILD="" JEDIT_BUILD_MODE="default" function getoptions() { OPTIND=1 while getopts "A:BFD:J:R:bd:fi:j:l:m:np:su" OPT do case "$OPT" in A) JEDIT_LOGIC_ANCESTOR="$OPTARG" ;; D) JAVA_ARGS["${#JAVA_ARGS[@]}"]="-D$OPTARG" ;; J) JAVA_ARGS["${#JAVA_ARGS[@]}"]="$OPTARG" ;; R) JEDIT_LOGIC="$OPTARG" JEDIT_LOGIC_REQUIREMENTS="true" ;; b) BUILD_ONLY=true ;; d) JEDIT_SESSION_DIRS="$JEDIT_SESSION_DIRS:$OPTARG" ;; i) if [ -z "$JEDIT_INCLUDE_SESSIONS" ]; then JEDIT_INCLUDE_SESSIONS="$OPTARG" else JEDIT_INCLUDE_SESSIONS="$JEDIT_INCLUDE_SESSIONS:$OPTARG" fi ;; f) FRESH_BUILD="true" ;; j) ARGS["${#ARGS[@]}"]="$OPTARG" ;; l) JEDIT_LOGIC="$OPTARG" ;; m) if [ -z "$JEDIT_PRINT_MODE" ]; then JEDIT_PRINT_MODE="$OPTARG" else JEDIT_PRINT_MODE="$JEDIT_PRINT_MODE,$OPTARG" fi ;; n) JEDIT_NO_BUILD="true" ;; p) ML_PROCESS_POLICY="$OPTARG" ;; s) JEDIT_BUILD_MODE="system" ;; u) JEDIT_BUILD_MODE="user" ;; \?) usage ;; esac done } eval "declare -a JAVA_ARGS=($JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)" declare -a ARGS=() declare -a OPTIONS; eval "OPTIONS=($ISABELLE_JEDIT_OPTIONS)" getoptions "${OPTIONS[@]}" getoptions "$@" shift $(($OPTIND - 1)) # args while [ "$#" -gt 0 ]; do ARGS["${#ARGS[@]}"]="$(platform_path "$1")" shift done -## dependencies - -if [ -e "$ISABELLE_HOME/Admin/build" ]; then - isabelle browser -b || exit $? - if [ -n "$FRESH_BUILD" ]; then - "$ISABELLE_HOME/Admin/build" jars_fresh || exit $? - else - "$ISABELLE_HOME/Admin/build" jars || exit $? - fi -elif [ -n "$FRESH_BUILD" ]; then - echo >&2 "### Ignoring fresh build option: not a repository clone" - FRESH_BUILD="" -fi - - -# target - -pushd "$ISABELLE_HOME" >/dev/null || failed - -TARGET_DIR="src/Tools/jEdit/dist" -TARGET_JAR0="$TARGET_DIR/jars/Isabelle-jEdit-base.jar" -TARGET_JAR="$TARGET_DIR/jars/Isabelle-jEdit.jar" -TARGET_SHASUM="$TARGET_DIR/Isabelle-jEdit.shasum" - -declare -a TARGET_DEPS=("lib/classes/Pure.jar" "$TARGET_DIR/jedit.jar") - -splitarray ":" "$ISABELLE_JEDIT_JARS" -for DEP in "${SPLITARRAY[@]}" -do - TARGET_DEPS["${#TARGET_DEPS[@]}"]="$TARGET_DIR/jars/$(basename "$DEP")" -done - -function target_shasum() -( - shasum -a1 -b "$TARGET_JAR0" "$TARGET_JAR" "${TARGET_DEPS[@]}" \ - "${SOURCES0[@]}" "${RESOURCES0[@]}" "${SOURCES[@]}" "${RESOURCES[@]}" 2>/dev/null -) - -function target_clean() -{ - rm -rf "$ISABELLE_HOME/$TARGET_DIR" -} - -[ -n "$FRESH_BUILD" ] && target_clean - - -## build - -BUILD_DIR="$TARGET_DIR/build" - -function init_resources () -{ - mkdir -p "$BUILD_DIR" || failed - cp -p -R "$@" "$BUILD_DIR/." -} - -function compile_sources() -{ - ( - #FIXME workarounds for scalac 2.11.0 - export CYGWIN="nodosfilewarning" - function stty() { :; } - export -f stty - - for DEP in "${TARGET_DEPS[@]}" - do - classpath "$DEP" - done - export CLASSPATH="$(platform_path "$ISABELLE_CLASSPATH")" - isabelle_scala scalac $ISABELLE_SCALAC_OPTIONS -d "$BUILD_DIR" "$@" - ) || fail "Failed to compile sources" -} - -function make_jar() -{ - isabelle_jdk jar -c -f "$1" -C "$BUILD_DIR" . || failed - rm -rf "$ISABELLE_HOME/$BUILD_DIR" -} - -target_shasum | cmp "$TARGET_SHASUM" >/dev/null 2>/dev/null -if [ -e "$ISABELLE_HOME/Admin/build" -a "$?" -ne 0 ]; then - echo "### Building Isabelle/jEdit ..." - - target_clean || failed - mkdir -p "$TARGET_DIR" || failed - - cp -p -R "$ISABELLE_JEDIT_HOME/." "$TARGET_DIR/." - - init_resources "${RESOURCES0[@]}" - compile_sources "${SOURCES0[@]}" - make_jar "$TARGET_JAR0" - classpath "$TARGET_JAR0" - - init_resources "${RESOURCES[@]}" - compile_sources "${SOURCES[@]}" - make_jar "$TARGET_JAR" - - target_shasum > "$TARGET_SHASUM" -fi - - ## main -popd >/dev/null +isabelle_admin_build browser || exit "$?" + +if [ -n "$FRESH_BUILD" ]; then + isabelle_admin_build jars_fresh || exit "$?" +else + isabelle_admin_build jars || exit "$?" +fi if [ "$BUILD_ONLY" = false ] then "$ISABELLE_HOME/lib/scripts/java-gui-setup" export JEDIT_SESSION_DIRS JEDIT_LOGIC JEDIT_LOGIC_ANCESTOR JEDIT_LOGIC_REQUIREMENTS \ JEDIT_INCLUDE_SESSIONS JEDIT_PRINT_MODE JEDIT_NO_BUILD JEDIT_BUILD_MODE export JEDIT_ML_PROCESS_POLICY="$ML_PROCESS_POLICY" - classpath "$JEDIT_HOME/dist/jedit.jar" exec isabelle java -splash:"$(platform_path "$ISABELLE_HOME/lib/logo/isabelle.gif")" \ - "${JAVA_ARGS[@]}" isabelle.Main "${ARGS[@]}" + "${JAVA_ARGS[@]}" isabelle.jedit.Main "${ARGS[@]}" fi diff --git a/src/Tools/jEdit/lib/Tools/jedit_client b/src/Tools/jEdit/lib/Tools/jedit_client --- a/src/Tools/jEdit/lib/Tools/jedit_client +++ b/src/Tools/jEdit/lib/Tools/jedit_client @@ -1,108 +1,107 @@ #!/usr/bin/env bash # # Author: Makarius # # DESCRIPTION: Isabelle/jEdit client for already running application ## settings SERVER_NAME="${ISABELLE_IDENTIFIER:-Isabelle}" eval "declare -a JAVA_ARGS=($JEDIT_JAVA_SYSTEM_OPTIONS $JEDIT_JAVA_OPTIONS)" ## diagnostics PRG="$(basename "$0")" function usage() { echo echo "Usage: isabelle $PRG [OPTIONS] [FILES ...]" echo echo " Options are:" echo " -c only check presence of server" echo " -n only report server name" echo " -s NAME server name (default \"$SERVER_NAME\")" echo echo " Connect to already running Isabelle/jEdit instance and open FILES" echo exit 1 } function fail() { echo "$1" >&2 exit 2 } function failed() { fail "Failed!" } ## process command line # options CHECK_ONLY="false" NAME_ONLY="false" while getopts "cns:" OPT do case "$OPT" in c) CHECK_ONLY="true" ;; n) NAME_ONLY="true" ;; s) SERVER_NAME="$OPTARG" ;; \?) usage ;; esac done shift $(($OPTIND - 1)) # args declare -a ARGS=() while [ "$#" -gt 0 ] do ARGS["${#ARGS[@]}"]="$(platform_path "$1")" shift done ## main if [ "$CHECK_ONLY" = true ] then [ -f "$JEDIT_SETTINGS/$SERVER_NAME" ] exit $? fi if [ "$NAME_ONLY" = true ] then echo "$SERVER_NAME" exit fi isabelle jedit -b || exit $? if [ -f "$JEDIT_SETTINGS/$SERVER_NAME" ] then - exec isabelle java "${JAVA_ARGS[@]}" \ - -jar $(platform_path "$JEDIT_HOME/dist/jedit.jar") \ + exec isabelle java "${JAVA_ARGS[@]}" org.gjt.sp.jedit.jEdit \ "-settings=$(platform_path "$JEDIT_SETTINGS")" \ -server="$SERVER_NAME" -reuseview "${ARGS[@]}" else fail "Isabelle/jEdit server \"$SERVER_NAME\" not active" fi diff --git a/src/Tools/jEdit/src-base/isabelle_encoding.scala b/src/Tools/jEdit/src-base/isabelle_encoding.scala deleted file mode 100644 --- a/src/Tools/jEdit/src-base/isabelle_encoding.scala +++ /dev/null @@ -1,59 +0,0 @@ -/* Title: Tools/jEdit/src-base/isabelle_encoding.scala - Author: Makarius - -Isabelle encoding -- based on UTF-8. -*/ - -package isabelle.jedit_base - - -import isabelle._ - -import org.gjt.sp.jedit.io.Encoding - -import java.nio.charset.{Charset, CodingErrorAction, CharacterCodingException} -import java.io.{InputStream, OutputStream, Reader, Writer, InputStreamReader, OutputStreamWriter, - CharArrayReader, ByteArrayOutputStream} - -import scala.io.{Codec, BufferedSource} - - -class Isabelle_Encoding extends Encoding -{ - private val BUFSIZE = 32768 - - private def text_reader(in: InputStream, codec: Codec, strict: Boolean): Reader = - { - val source = (new BufferedSource(in)(codec)).mkString - - if (strict && Codepoint.iterator(source).exists(Symbol.is_code)) - throw new CharacterCodingException() - - new CharArrayReader(Symbol.decode(source).toArray) - } - - override def getTextReader(in: InputStream): Reader = - text_reader(in, UTF8.codec(), true) - - override def getPermissiveTextReader(in: InputStream): Reader = - { - val codec = UTF8.codec() - codec.onMalformedInput(CodingErrorAction.REPLACE) - codec.onUnmappableCharacter(CodingErrorAction.REPLACE) - text_reader(in, codec, false) - } - - override def getTextWriter(out: OutputStream): Writer = - { - val buffer = new ByteArrayOutputStream(BUFSIZE) { - override def flush(): Unit = - { - val text = Symbol.encode(toString(UTF8.charset_name)) - out.write(UTF8.bytes(text)) - out.flush() - } - override def close(): Unit = out.close() - } - new OutputStreamWriter(buffer, UTF8.charset.newEncoder()) - } -} diff --git a/src/Tools/jEdit/src-base/jedit_lib.scala b/src/Tools/jEdit/src-base/jedit_lib.scala deleted file mode 100644 --- a/src/Tools/jEdit/src-base/jedit_lib.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* Title: Tools/jEdit/src-base/jedit_lib.scala - Author: Makarius - -Misc library functions for jEdit. -*/ - -package isabelle.jedit_base - - -import isabelle._ - -import org.gjt.sp.jedit.{jEdit, View} - - -object JEdit_Lib -{ - def request_focus_view(alt_view: View = null): Unit = - { - val view = if (alt_view != null) alt_view else jEdit.getActiveView() - if (view != null) { - val text_area = view.getTextArea - if (text_area != null) text_area.requestFocus() - } - } -} \ No newline at end of file diff --git a/src/Tools/jEdit/src-base/syntax_style.scala b/src/Tools/jEdit/src-base/syntax_style.scala deleted file mode 100644 --- a/src/Tools/jEdit/src-base/syntax_style.scala +++ /dev/null @@ -1,41 +0,0 @@ -/* Title: Tools/jEdit/src-base/syntax_style.scala - Author: Makarius - -Extended syntax styles: dummy version. -*/ - -package isabelle.jedit_base - - -import isabelle._ - -import org.gjt.sp.util.SyntaxUtilities -import org.gjt.sp.jedit.syntax.{Token => JEditToken, SyntaxStyle} -import org.gjt.sp.jedit.jEdit - - -object Syntax_Style -{ - private val plain_range: Int = JEditToken.ID_COUNT - private val full_range = 6 * plain_range + 1 - - object Dummy_Extender extends SyntaxUtilities.StyleExtender - { - override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = - { - val new_styles = new Array[SyntaxStyle](full_range) - for (i <- 0 until full_range) { - new_styles(i) = styles(i % plain_range) - } - new_styles - } - } - - def set_style_extender(extender: SyntaxUtilities.StyleExtender): Unit = - { - SyntaxUtilities.setStyleExtender(extender) - GUI_Thread.later { jEdit.propertiesChanged } - } - - def dummy_style_extender(): Unit = set_style_extender(Dummy_Extender) -} diff --git a/src/Tools/jEdit/src-base/plugin.scala b/src/Tools/jEdit/src/base_plugin.scala rename from src/Tools/jEdit/src-base/plugin.scala rename to src/Tools/jEdit/src/base_plugin.scala --- a/src/Tools/jEdit/src-base/plugin.scala +++ b/src/Tools/jEdit/src/base_plugin.scala @@ -1,35 +1,35 @@ -/* Title: Tools/jEdit/src-base/plugin.scala +/* Title: Tools/jEdit/src/base_plugin.scala Author: Makarius Isabelle base environment for jEdit. */ -package isabelle.jedit_base +package isabelle.jedit import isabelle._ import org.gjt.sp.jedit.{EBMessage, Debug, EBPlugin} import org.gjt.sp.util.SyntaxUtilities -class Plugin extends EBPlugin +class Base_Plugin extends EBPlugin { override def start(): Unit = { Isabelle_System.init() GUI.use_isabelle_fonts() Debug.DISABLE_SEARCH_DIALOG_POOL = true - Syntax_Style.dummy_style_extender() + Syntax_Style.set_extender(Syntax_Style.Base_Extender) } override def stop(): Unit = { - Syntax_Style.set_style_extender(new SyntaxUtilities.StyleExtender) + Syntax_Style.set_extender(new SyntaxUtilities.StyleExtender) } override def handleMessage(message: EBMessage): Unit = {} } diff --git a/src/Tools/jEdit/src/debugger_dockable.scala b/src/Tools/jEdit/src/debugger_dockable.scala --- a/src/Tools/jEdit/src/debugger_dockable.scala +++ b/src/Tools/jEdit/src/debugger_dockable.scala @@ -1,372 +1,371 @@ /* Title: Tools/jEdit/src/debugger_dockable.scala Author: Makarius Dockable window for Isabelle/ML debugger. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.{BorderLayout, Dimension} import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent, FocusAdapter, FocusEvent, MouseEvent, MouseAdapter} import javax.swing.{JTree, JMenuItem} import javax.swing.tree.{DefaultMutableTreeNode, DefaultTreeModel, TreeSelectionModel} import javax.swing.event.{TreeSelectionEvent, TreeSelectionListener} import scala.collection.immutable.SortedMap import scala.swing.{Button, Label, Component, ScrollPane, SplitPane, Orientation, CheckBox, BorderPanel} import scala.swing.event.ButtonClicked import org.gjt.sp.jedit.{jEdit, View} import org.gjt.sp.jedit.menu.EnhancedMenuItem import org.gjt.sp.jedit.textarea.JEditTextArea object Debugger_Dockable { /* breakpoints */ def get_breakpoint(text_area: JEditTextArea, offset: Text.Offset): Option[(Command, Long)] = { GUI_Thread.require {} Document_View.get(text_area) match { case Some(doc_view) => val rendering = doc_view.get_rendering() val range = JEdit_Lib.point_range(text_area.getBuffer, offset) rendering.breakpoint(range) case None => None } } /* context menu */ def context_menu(text_area: JEditTextArea, offset: Text.Offset): List[JMenuItem] = if (PIDE.session.debugger.is_active() && get_breakpoint(text_area, offset).isDefined) { val context = jEdit.getActionContext() val name = "isabelle.toggle-breakpoint" List(new EnhancedMenuItem(context.getAction(name).getLabel, name, context)) } else Nil } class Debugger_Dockable(view: View, position: String) extends Dockable(view, position) { GUI_Thread.require {} private val debugger = PIDE.session.debugger /* component state -- owned by GUI thread */ private var current_snapshot = Document.Snapshot.init private var current_threads: Debugger.Threads = SortedMap.empty private var current_output: List[XML.Tree] = Nil /* pretty text area */ val pretty_text_area = new Pretty_Text_Area(view) override def detach_operation: Option[() => Unit] = pretty_text_area.detach_operation private def handle_resize(): Unit = { GUI_Thread.require {} pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } private def handle_update(): Unit = { GUI_Thread.require {} val new_snapshot = PIDE.editor.current_node_snapshot(view).getOrElse(current_snapshot) val (new_threads, new_output) = debugger.status(tree_selection()) if (new_threads != current_threads) update_tree(new_threads) if (new_output != current_output) pretty_text_area.update(new_snapshot, Command.Results.empty, Pretty.separate(new_output)) current_snapshot = new_snapshot current_threads = new_threads current_output = new_output } /* tree view */ private val root = new DefaultMutableTreeNode("Threads") val tree = new JTree(root) tree.setRowHeight(0) tree.getSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION) def tree_selection(): Option[Debugger.Context] = tree.getLastSelectedPathComponent match { case node: DefaultMutableTreeNode => node.getUserObject match { case c: Debugger.Context => Some(c) case _ => None } case _ => None } def thread_selection(): Option[String] = tree_selection().map(_.thread_name) private def update_tree(threads: Debugger.Threads): Unit = { val thread_contexts = (for ((a, b) <- threads.iterator) yield Debugger.Context(a, b)).toList val new_tree_selection = tree_selection() match { case Some(c) if thread_contexts.contains(c) => Some(c) case Some(c) if thread_contexts.exists(t => c.thread_name == t.thread_name) => Some(c.reset) case _ => thread_contexts.headOption } tree.clearSelection root.removeAllChildren for (thread <- thread_contexts) { val thread_node = new DefaultMutableTreeNode(thread) for ((debug_state, i) <- thread.debug_states.zipWithIndex) thread_node.add(new DefaultMutableTreeNode(thread.select(i))) root.add(thread_node) } tree.getModel.asInstanceOf[DefaultTreeModel].reload(root) tree.expandRow(0) for (i <- Range.inclusive(tree.getRowCount - 1, 1, -1)) tree.expandRow(i) new_tree_selection match { case Some(c) => val i = (for (t <- thread_contexts.iterator.takeWhile(t => c.thread_name != t.thread_name)) yield t.size).sum tree.addSelectionRow(i + c.index + 1) case None => } tree.revalidate() } def update_vals(): Unit = { tree_selection() match { case Some(c) if c.stack_state.isDefined => debugger.print_vals(c, sml_button.selected, context_field.getText) case Some(c) => debugger.clear_output(c.thread_name) case None => } } tree.addTreeSelectionListener( new TreeSelectionListener { override def valueChanged(e: TreeSelectionEvent): Unit = { update_focus() update_vals() } }) tree.addMouseListener( new MouseAdapter { override def mouseClicked(e: MouseEvent): Unit = { val click = tree.getPathForLocation(e.getX, e.getY) if (click != null && e.getClickCount == 1) update_focus() } }) private val tree_pane = new ScrollPane(Component.wrap(tree)) tree_pane.horizontalScrollBarPolicy = ScrollPane.BarPolicy.Always tree_pane.verticalScrollBarPolicy = ScrollPane.BarPolicy.Always tree_pane.minimumSize = new Dimension(200, 100) /* controls */ private val break_button = new CheckBox("Break") { tooltip = "Break running threads at next possible breakpoint" selected = debugger.is_break() reactions += { case ButtonClicked(_) => debugger.set_break(selected) } } private val continue_button = new Button("Continue") { tooltip = "Continue program on current thread, until next breakpoint" reactions += { case ButtonClicked(_) => thread_selection().map(debugger.continue) } } private val step_button = new Button("Step") { tooltip = "Single-step in depth-first order" reactions += { case ButtonClicked(_) => thread_selection().map(debugger.step) } } private val step_over_button = new Button("Step over") { tooltip = "Single-step within this function" reactions += { case ButtonClicked(_) => thread_selection().map(debugger.step_over) } } private val step_out_button = new Button("Step out") { tooltip = "Single-step outside this function" reactions += { case ButtonClicked(_) => thread_selection().map(debugger.step_out) } } private val context_label = new Label("Context:") { tooltip = "Isabelle/ML context: type theory, Proof.context, Context.generic" } private val context_field = new Completion_Popup.History_Text_Field("isabelle-debugger-context") { override def processKeyEvent(evt: KeyEvent): Unit = { if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) eval_expression() super.processKeyEvent(evt) } setColumns(20) setToolTipText(context_label.tooltip) setFont(GUI.imitate_font(getFont, scale = 1.2)) } private val expression_label = new Label("ML:") { tooltip = "Isabelle/ML or Standard ML expression" } private val expression_field = new Completion_Popup.History_Text_Field("isabelle-debugger-expression") { override def processKeyEvent(evt: KeyEvent): Unit = { if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) eval_expression() super.processKeyEvent(evt) } { val max = getPreferredSize; max.width = Integer.MAX_VALUE; setMaximumSize(max) } setColumns(40) setToolTipText(expression_label.tooltip) setFont(GUI.imitate_font(getFont, scale = 1.2)) } private val eval_button = new Button("Eval") { tooltip = "Evaluate ML expression within optional context" reactions += { case ButtonClicked(_) => eval_expression() } } private def eval_expression(): Unit = { context_field.addCurrentToHistory() expression_field.addCurrentToHistory() tree_selection() match { case Some(c) if c.debug_index.isDefined => debugger.eval(c, sml_button.selected, context_field.getText, expression_field.getText) case _ => } } private val sml_button = new CheckBox("SML") { tooltip = "Official Standard ML instead of Isabelle/ML" selected = false } private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private val controls = Wrap_Panel( List( break_button, continue_button, step_button, step_over_button, step_out_button, context_label, Component.wrap(context_field), expression_label, Component.wrap(expression_field), eval_button, sml_button, pretty_text_area.search_label, pretty_text_area.search_field, zoom)) add(controls.peer, BorderLayout.NORTH) /* focus */ override def focusOnDefaultComponent(): Unit = eval_button.requestFocus() addFocusListener(new FocusAdapter { override def focusGained(e: FocusEvent): Unit = update_focus() }) private def update_focus(): Unit = { for (c <- tree_selection()) { debugger.set_focus(c) for { pos <- c.debug_position link <- PIDE.editor.hyperlink_position(false, current_snapshot, pos) } link.follow(view) } JEdit_Lib.jedit_text_areas(view.getBuffer).foreach(_.repaint()) } /* main panel */ val main_panel: SplitPane = new SplitPane(Orientation.Vertical) { oneTouchExpandable = true leftComponent = tree_pane rightComponent = Component.wrap(pretty_text_area) } set_content(main_panel) /* main */ private val main = Session.Consumer[Any](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() } case Debugger.Update => GUI_Thread.later { break_button.selected = debugger.is_break() handle_update() } } override def init(): Unit = { PIDE.session.global_options += main PIDE.session.debugger_updates += main debugger.init() handle_update() jEdit.propertiesChanged() } override def exit(): Unit = { PIDE.session.global_options -= main PIDE.session.debugger_updates -= main delay_resize.revoke() debugger.exit() jEdit.propertiesChanged() } /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) } diff --git a/src/Tools/jEdit/src-base/dockable.scala b/src/Tools/jEdit/src/dockable.scala rename from src/Tools/jEdit/src-base/dockable.scala rename to src/Tools/jEdit/src/dockable.scala --- a/src/Tools/jEdit/src-base/dockable.scala +++ b/src/Tools/jEdit/src/dockable.scala @@ -1,46 +1,46 @@ -/* Title: Tools/jEdit/src-base/dockable.scala +/* Title: Tools/jEdit/src/dockable.scala Author: Makarius Generic dockable window. */ -package isabelle.jedit_base +package isabelle.jedit import isabelle._ import java.awt.{Dimension, Component, BorderLayout} import javax.swing.JPanel import org.gjt.sp.jedit.View import org.gjt.sp.jedit.gui.{DefaultFocusComponent, DockableWindowManager} class Dockable(view: View, position: String) extends JPanel(new BorderLayout) with DefaultFocusComponent { if (position == DockableWindowManager.FLOATING) setPreferredSize(new Dimension(500, 250)) def focusOnDefaultComponent(): Unit = JEdit_Lib.request_focus_view(view) def set_content(c: Component): Unit = add(c, BorderLayout.CENTER) def set_content(c: scala.swing.Component): Unit = add(c.peer, BorderLayout.CENTER) def detach_operation: Option[() => Unit] = None protected def init(): Unit = {} protected def exit(): Unit = {} override def addNotify(): Unit = { super.addNotify() init() } override def removeNotify(): Unit = { exit() super.removeNotify() } } diff --git a/src/Tools/jEdit/src/documentation_dockable.scala b/src/Tools/jEdit/src/documentation_dockable.scala --- a/src/Tools/jEdit/src/documentation_dockable.scala +++ b/src/Tools/jEdit/src/documentation_dockable.scala @@ -1,115 +1,114 @@ /* Title: Tools/jEdit/src/documentation_dockable.scala Author: Makarius Dockable window for Isabelle documentation. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.Dimension import java.awt.event.{KeyEvent, KeyAdapter, MouseEvent, MouseAdapter} import javax.swing.{JTree, JScrollPane} import javax.swing.tree.{DefaultMutableTreeNode, TreeSelectionModel} import org.gjt.sp.jedit.{View, OperatingSystem} class Documentation_Dockable(view: View, position: String) extends Dockable(view, position) { private val docs = Doc.contents() private case class Documentation(name: String, title: String, path: Path) { override def toString: String = "" + HTML.output(name) + ": " + HTML.output(title) + "" } private case class Text_File(name: String, path: Path) { override def toString: String = name } private val root = new DefaultMutableTreeNode docs foreach { case Doc.Section(text, _) => root.add(new DefaultMutableTreeNode(text)) case Doc.Doc(name, title, path) => root.getLastChild.asInstanceOf[DefaultMutableTreeNode] .add(new DefaultMutableTreeNode(Documentation(name, title, path))) case Doc.Text_File(name: String, path: Path) => root.getLastChild.asInstanceOf[DefaultMutableTreeNode] .add(new DefaultMutableTreeNode(Text_File(name, path.expand))) } private val tree = new JTree(root) tree.setRowHeight(0) tree.getSelectionModel.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION) override def focusOnDefaultComponent(): Unit = tree.requestFocusInWindow private def action(node: DefaultMutableTreeNode): Unit = { node.getUserObject match { case Text_File(_, path) => PIDE.editor.goto_file(true, view, File.platform_path(path)) case Documentation(_, _, path) => PIDE.editor.goto_doc(view, path) case _ => } } tree.addKeyListener(new KeyAdapter { override def keyPressed(e: KeyEvent): Unit = { if (e.getKeyCode == KeyEvent.VK_ENTER) { e.consume val path = tree.getSelectionPath if (path != null) { path.getLastPathComponent match { case node: DefaultMutableTreeNode => action(node) case _ => } } } } }) tree.addMouseListener(new MouseAdapter { override def mouseClicked(e: MouseEvent): Unit = { val click = tree.getPathForLocation(e.getX, e.getY) if (click != null && e.getClickCount == 1) { (click.getLastPathComponent, tree.getLastSelectedPathComponent) match { case (node: DefaultMutableTreeNode, node1: DefaultMutableTreeNode) if node == node1 => action(node) case _ => } } } }) { var expand = true var visible = 0 def make_visible(row: Int): Unit = { visible += 1; tree.expandRow(row) } for ((entry, row) <- docs.zipWithIndex) { entry match { case Doc.Section(_, important) => expand = important make_visible(row) case _ => if (expand) make_visible(row) } } tree.setRootVisible(false) tree.setVisibleRowCount(visible) } private val tree_view = new JScrollPane(tree) tree_view.setMinimumSize(new Dimension(200, 50)) set_content(tree_view) } diff --git a/src/Tools/jEdit/src/graphview_dockable.scala b/src/Tools/jEdit/src/graphview_dockable.scala --- a/src/Tools/jEdit/src/graphview_dockable.scala +++ b/src/Tools/jEdit/src/graphview_dockable.scala @@ -1,167 +1,166 @@ /* Title: Tools/jEdit/src/graphview_dockable.scala Author: Makarius Stateless dockable window for graphview. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import javax.swing.JComponent import java.awt.{Point, Font} import java.awt.event.{WindowFocusListener, WindowEvent} import org.gjt.sp.jedit.View import scala.swing.TextArea object Graphview_Dockable { /* implicit arguments -- owned by GUI thread */ private var implicit_snapshot = Document.Snapshot.init private val no_graph: Exn.Result[Graph_Display.Graph] = Exn.Exn(ERROR("No graph")) private var implicit_graph = no_graph private def set_implicit( snapshot: Document.Snapshot, graph: Exn.Result[Graph_Display.Graph]): Unit = { GUI_Thread.require {} implicit_snapshot = snapshot implicit_graph = graph } private def reset_implicit(): Unit = set_implicit(Document.Snapshot.init, no_graph) class Handler extends Active.Handler { override def handle( view: View, text: String, elem: XML.Elem, doc_view: Document_View, snapshot: Document.Snapshot): Boolean = { elem match { case XML.Elem(Markup(Markup.GRAPHVIEW, _), body) => Isabelle_Thread.fork(name = "graphview") { val graph = Exn.capture { Graph_Display.decode_graph(body).transitive_reduction_acyclic } GUI_Thread.later { set_implicit(snapshot, graph) view.getDockableWindowManager.floatDockableWindow("isabelle-graphview") } } true case _ => false } } } } class Graphview_Dockable(view: View, position: String) extends Dockable(view, position) { private val snapshot = Graphview_Dockable.implicit_snapshot private val graph_result = Graphview_Dockable.implicit_graph private val window_focus_listener = new WindowFocusListener { def windowGainedFocus(e: WindowEvent): Unit = Graphview_Dockable.set_implicit(snapshot, graph_result) def windowLostFocus(e: WindowEvent): Unit = Graphview_Dockable.reset_implicit() } val graphview = graph_result match { case Exn.Res(graph) => val graphview = new isabelle.graphview.Graphview(graph) { def options: Options = PIDE.options.value override def make_tooltip(parent: JComponent, x: Int, y: Int, body: XML.Body): String = { Pretty_Tooltip.invoke(() => { val model = File_Model.empty(PIDE.session) val rendering = JEdit_Rendering(snapshot, model, options) val info = Text.Info(Text.Range.offside, body) Pretty_Tooltip(view, parent, new Point(x, y), rendering, Command.Results.empty, info) }) null } override def make_font(): Font = if (editor_style) GUI.imitate_font(Font_Info.main().font) else GUI.imitate_font(Font_Info.main().font, family = options.string("graphview_font_family"), scale = options.real("graphview_font_scale")) override def foreground_color = if (editor_style) view.getTextArea.getPainter.getForeground else super.foreground_color override def background_color = if (editor_style) view.getTextArea.getPainter.getBackground else super.background_color override def selection_color = if (editor_style) view.getTextArea.getPainter.getSelectionColor else super.selection_color override def highlight_color = if (editor_style) view.getTextArea.getPainter.getLineHighlightColor else super.highlight_color override def error_color = PIDE.options.color_value("error_color") editor_style = true } new isabelle.graphview.Main_Panel(graphview) case Exn.Exn(exn) => new TextArea(Exn.message(exn)) } set_content(graphview) override def focusOnDefaultComponent(): Unit = { graphview match { case main_panel: isabelle.graphview.Main_Panel => main_panel.tree_panel.tree.requestFocusInWindow case _ => } } /* main */ private val main = Session.Consumer[Session.Global_Options](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { graphview match { case main_panel: isabelle.graphview.Main_Panel => main_panel.update_layout() case _ => } } } override def init(): Unit = { GUI.parent_window(this).foreach(_.addWindowFocusListener(window_focus_listener)) PIDE.session.global_options += main } override def exit(): Unit = { GUI.parent_window(this).foreach(_.removeWindowFocusListener(window_focus_listener)) PIDE.session.global_options -= main } } diff --git a/src/Tools/jEdit/src/info_dockable.scala b/src/Tools/jEdit/src/info_dockable.scala --- a/src/Tools/jEdit/src/info_dockable.scala +++ b/src/Tools/jEdit/src/info_dockable.scala @@ -1,120 +1,119 @@ /* Title: Tools/jEdit/src/info_dockable.scala Author: Makarius Dockable window with info text. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.BorderLayout import java.awt.event.{ComponentEvent, ComponentAdapter, WindowFocusListener, WindowEvent} import org.gjt.sp.jedit.View object Info_Dockable { /* implicit arguments -- owned by GUI thread */ private var implicit_snapshot = Document.Snapshot.init private var implicit_results = Command.Results.empty private var implicit_info: XML.Body = Nil private def set_implicit( snapshot: Document.Snapshot, results: Command.Results, info: XML.Body): Unit = { GUI_Thread.require {} implicit_snapshot = snapshot implicit_results = results implicit_info = info } private def reset_implicit(): Unit = set_implicit(Document.Snapshot.init, Command.Results.empty, Nil) def apply( view: View, snapshot: Document.Snapshot, results: Command.Results, info: XML.Body): Unit = { set_implicit(snapshot, results, info) view.getDockableWindowManager.floatDockableWindow("isabelle-info") } } class Info_Dockable(view: View, position: String) extends Dockable(view, position) { /* component state -- owned by GUI thread */ private val snapshot = Info_Dockable.implicit_snapshot private val results = Info_Dockable.implicit_results private val info = Info_Dockable.implicit_info private val window_focus_listener = new WindowFocusListener { def windowGainedFocus(e: WindowEvent): Unit = Info_Dockable.set_implicit(snapshot, results, info) def windowLostFocus(e: WindowEvent): Unit = Info_Dockable.reset_implicit() } /* pretty text area */ private val pretty_text_area = new Pretty_Text_Area(view) set_content(pretty_text_area) pretty_text_area.update(snapshot, results, info) private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private def handle_resize(): Unit = { GUI_Thread.require {} pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) private val controls = Wrap_Panel(List(pretty_text_area.search_label, pretty_text_area.search_field, zoom)) add(controls.peer, BorderLayout.NORTH) /* main */ private val main = Session.Consumer[Session.Global_Options](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() } } override def init(): Unit = { GUI.parent_window(this).map(_.addWindowFocusListener(window_focus_listener)) PIDE.session.global_options += main handle_resize() } override def exit(): Unit = { GUI.parent_window(this).map(_.removeWindowFocusListener(window_focus_listener)) PIDE.session.global_options -= main delay_resize.revoke() } } diff --git a/src/Tools/jEdit/src/isabelle_encoding.scala b/src/Tools/jEdit/src/isabelle_encoding.scala --- a/src/Tools/jEdit/src/isabelle_encoding.scala +++ b/src/Tools/jEdit/src/isabelle_encoding.scala @@ -1,22 +1,69 @@ /* Title: Tools/jEdit/src/isabelle_encoding.scala Author: Makarius Isabelle encoding -- based on UTF-8. */ package isabelle.jedit import isabelle._ import org.gjt.sp.jedit.buffer.JEditBuffer +import org.gjt.sp.jedit.io.Encoding + +import java.nio.charset.{Charset, CodingErrorAction, CharacterCodingException} +import java.io.{InputStream, OutputStream, Reader, Writer, InputStreamReader, OutputStreamWriter, + CharArrayReader, ByteArrayOutputStream} + +import scala.io.{Codec, BufferedSource} object Isabelle_Encoding { def is_active(buffer: JEditBuffer): Boolean = buffer.getStringProperty(JEditBuffer.ENCODING).asInstanceOf[String] == "UTF-8-Isabelle" def perhaps_decode(buffer: JEditBuffer, s: String): String = if (is_active(buffer)) Symbol.decode(s) else s } + +class Isabelle_Encoding extends Encoding +{ + private val BUFSIZE = 32768 + + private def text_reader(in: InputStream, codec: Codec, strict: Boolean): Reader = + { + val source = (new BufferedSource(in)(codec)).mkString + + if (strict && Codepoint.iterator(source).exists(Symbol.is_code)) + throw new CharacterCodingException() + + new CharArrayReader(Symbol.decode(source).toArray) + } + + override def getTextReader(in: InputStream): Reader = + text_reader(in, UTF8.codec(), true) + + override def getPermissiveTextReader(in: InputStream): Reader = + { + val codec = UTF8.codec() + codec.onMalformedInput(CodingErrorAction.REPLACE) + codec.onUnmappableCharacter(CodingErrorAction.REPLACE) + text_reader(in, codec, false) + } + + override def getTextWriter(out: OutputStream): Writer = + { + val buffer = new ByteArrayOutputStream(BUFSIZE) { + override def flush(): Unit = + { + val text = Symbol.encode(toString(UTF8.charset_name)) + out.write(UTF8.bytes(text)) + out.flush() + } + override def close(): Unit = out.close() + } + new OutputStreamWriter(buffer, UTF8.charset.newEncoder()) + } +} diff --git a/src/Tools/jEdit/src/jedit_bibtex.scala b/src/Tools/jEdit/src/jedit_bibtex.scala --- a/src/Tools/jEdit/src/jedit_bibtex.scala +++ b/src/Tools/jEdit/src/jedit_bibtex.scala @@ -1,199 +1,156 @@ /* Title: Tools/jEdit/src/jedit_bibtex.scala Author: Makarius BibTeX support in Isabelle/jEdit. */ package isabelle.jedit import isabelle._ import scala.collection.mutable import java.awt.event.{ActionListener, ActionEvent} import javax.swing.text.Segment import javax.swing.tree.DefaultMutableTreeNode import javax.swing.{JMenu, JMenuItem} import org.gjt.sp.jedit.Buffer import org.gjt.sp.jedit.textarea.{JEditTextArea, TextArea} import org.gjt.sp.jedit.syntax.{Token => JEditToken, TokenMarker, TokenHandler} -import sidekick.{SideKickParser, SideKickParsedData} - object JEdit_Bibtex { /** context menu **/ def context_menu(text_area0: JEditTextArea): List[JMenuItem] = { text_area0 match { case text_area: TextArea => text_area.getBuffer match { case buffer: Buffer if (Bibtex.is_bibtex(JEdit_Lib.buffer_name(buffer)) && buffer.isEditable) => val menu = new JMenu("BibTeX entries") for (entry <- Bibtex.known_entries) { val item = new JMenuItem(entry.kind) item.addActionListener(new ActionListener { def actionPerformed(evt: ActionEvent): Unit = Isabelle.insert_line_padding(text_area, entry.template) }) menu.add(item) } List(menu) case _ => Nil } case _ => Nil } } /** token markup **/ /* token style */ private def token_style(context: String, token: Bibtex.Token): Byte = token.kind match { case Bibtex.Token.Kind.COMMAND => JEditToken.KEYWORD2 case Bibtex.Token.Kind.ENTRY => JEditToken.KEYWORD1 case Bibtex.Token.Kind.KEYWORD => JEditToken.OPERATOR case Bibtex.Token.Kind.NAT => JEditToken.LITERAL2 case Bibtex.Token.Kind.STRING => JEditToken.LITERAL1 case Bibtex.Token.Kind.NAME => JEditToken.LABEL case Bibtex.Token.Kind.IDENT => if (Bibtex.is_month(token.source)) JEditToken.LITERAL3 else Bibtex.get_entry(context) match { case Some(entry) if entry.is_required(token.source) => JEditToken.KEYWORD3 case Some(entry) if entry.is_optional(token.source) => JEditToken.KEYWORD4 case _ => JEditToken.DIGIT } case Bibtex.Token.Kind.SPACE => JEditToken.NULL case Bibtex.Token.Kind.COMMENT => JEditToken.COMMENT1 case Bibtex.Token.Kind.ERROR => JEditToken.INVALID } /* line context */ private val mode_rule_set = Token_Markup.mode_rule_set("bibtex") private class Line_Context(val context: Option[Bibtex.Line_Context]) extends TokenMarker.LineContext(mode_rule_set, null) { override def hashCode: Int = context.hashCode override def equals(that: Any): Boolean = that match { case other: Line_Context => context == other.context case _ => false } } /* token marker */ class Token_Marker extends TokenMarker { addRuleSet(mode_rule_set) override def markTokens(context: TokenMarker.LineContext, handler: TokenHandler, raw_line: Segment): TokenMarker.LineContext = { val line_ctxt = context match { case c: Line_Context => c.context case _ => Some(Bibtex.Ignored) } val line = if (raw_line == null) new Segment else raw_line def no_markup = { val styled_token = (JEditToken.NULL, line.subSequence(0, line.count).toString) (List(styled_token), new Line_Context(None)) } val context1 = { val (styled_tokens, context1) = line_ctxt match { case Some(ctxt) => try { val (chunks, ctxt1) = Bibtex.parse_line(line, ctxt) val styled_tokens = for { chunk <- chunks; tok <- chunk.tokens } yield (token_style(chunk.kind, tok), tok.source) (styled_tokens, new Line_Context(Some(ctxt1))) } catch { case ERROR(msg) => Output.warning(msg); no_markup } case None => no_markup } var offset = 0 for ((style, token) <- styled_tokens) { val length = token.length val end_offset = offset + length if ((offset until end_offset).exists(i => line.charAt(i) == '\t')) { for (i <- offset until end_offset) handler.handleToken(line, style, i, 1, context1) } else handler.handleToken(line, style, offset, length, context1) offset += length } handler.handleToken(line, JEditToken.END, line.count, 0, context1) context1 } val context2 = context1.intern handler.setLineContext(context2) context2 } } - - - - /** Sidekick parser **/ - - class Sidekick_Parser extends SideKickParser("bibtex") - { - override def supportsCompletion = false - - private class Asset(label: String, label_html: String, range: Text.Range, source: String) - extends Isabelle_Sidekick.Asset(label, range) { - override def getShortString: String = label_html - override def getLongString: String = source - } - - def parse(buffer: Buffer, error_source: errorlist.DefaultErrorSource): SideKickParsedData = - { - val data = Isabelle_Sidekick.root_data(buffer) - - try { - var offset = 0 - for (chunk <- Bibtex.parse(JEdit_Lib.buffer_text(buffer))) { - val kind = chunk.kind - val name = chunk.name - val source = chunk.source - if (kind != "") { - val label = kind + (if (name == "") "" else " " + name) - val label_html = - "" + HTML.output(kind) + "" + - (if (name == "") "" else " " + HTML.output(name)) + "" - val range = Text.Range(offset, offset + source.length) - val asset = new Asset(label, label_html, range, source) - data.root.add(new DefaultMutableTreeNode(asset)) - } - offset += source.length - } - data - } - catch { case ERROR(msg) => Output.warning(msg); null } - } - } } diff --git a/src/Tools/jEdit/src/jedit_lib.scala b/src/Tools/jEdit/src/jedit_lib.scala --- a/src/Tools/jEdit/src/jedit_lib.scala +++ b/src/Tools/jEdit/src/jedit_lib.scala @@ -1,371 +1,377 @@ /* Title: Tools/jEdit/src/jedit_lib.scala Author: Makarius Misc library functions for jEdit. */ package isabelle.jedit import isabelle._ import java.io.{File => JFile} import java.awt.{Component, Container, GraphicsEnvironment, Point, Rectangle, Dimension, Toolkit} import java.awt.event.{InputEvent, KeyEvent, KeyListener} import javax.swing.{Icon, ImageIcon, JWindow, SwingUtilities} import scala.util.parsing.input.CharSequenceReader import scala.jdk.CollectionConverters._ import org.gjt.sp.jedit.{jEdit, Buffer, View, GUIUtilities, Debug, EditPane} import org.gjt.sp.jedit.io.{FileVFS, VFSManager} import org.gjt.sp.jedit.gui.{KeyEventWorkaround, KeyEventTranslator} import org.gjt.sp.jedit.buffer.{JEditBuffer, LineManager} import org.gjt.sp.jedit.textarea.{JEditTextArea, TextArea, TextAreaPainter} object JEdit_Lib { /* jEdit directories */ def directories: List[JFile] = (Option(jEdit.getSettingsDirectory).toList ::: List(jEdit.getJEditHome)).map(new JFile(_)) /* window geometry measurement */ private lazy val dummy_window = new JWindow final case class Window_Geometry(width: Int, height: Int, inner_width: Int, inner_height: Int) { def deco_width: Int = width - inner_width def deco_height: Int = height - inner_height } def window_geometry(outer: Container, inner: Component): Window_Geometry = { GUI_Thread.require {} val old_content = dummy_window.getContentPane dummy_window.setContentPane(outer) dummy_window.pack dummy_window.revalidate() val geometry = Window_Geometry( dummy_window.getWidth, dummy_window.getHeight, inner.getWidth, inner.getHeight) dummy_window.setContentPane(old_content) geometry } /* files */ def is_file(name: String): Boolean = VFSManager.getVFSForPath(name).isInstanceOf[FileVFS] def check_file(name: String): Option[JFile] = if (is_file(name)) Some(new JFile(name)) else None /* buffers */ def buffer_text(buffer: JEditBuffer): String = buffer_lock(buffer) { buffer.getText(0, buffer.getLength) } def buffer_reader(buffer: JEditBuffer): CharSequenceReader = Scan.char_reader(buffer.getSegment(0, buffer.getLength)) def buffer_mode(buffer: JEditBuffer): String = { val mode = buffer.getMode if (mode == null) "" else { val name = mode.getName if (name == null) "" else name } } def buffer_line_manager(buffer: JEditBuffer): LineManager = Untyped.get[LineManager](buffer, "lineMgr") def buffer_name(buffer: Buffer): String = buffer.getSymlinkPath def buffer_file(buffer: Buffer): Option[JFile] = check_file(buffer_name(buffer)) def buffer_undo_in_progress[A](buffer: JEditBuffer, body: => A): A = { val undo_in_progress = buffer.isUndoInProgress def set(b: Boolean): Unit = Untyped.set[Boolean](buffer, "undoInProgress", b) try { set(true); body } finally { set(undo_in_progress) } } /* main jEdit components */ def jedit_buffers(): Iterator[Buffer] = jEdit.getBufferManager().getBuffers().asScala.iterator def jedit_buffer(name: String): Option[Buffer] = jedit_buffers().find(buffer => buffer_name(buffer) == name) def jedit_buffer(name: Document.Node.Name): Option[Buffer] = jedit_buffer(name.node) def jedit_views(): Iterator[View] = jEdit.getViewManager().getViews().asScala.iterator def jedit_view(view: View = null): View = if (view == null) jEdit.getActiveView() else view def jedit_edit_panes(view: View): Iterator[EditPane] = if (view == null) Iterator.empty else view.getEditPanes().iterator.filter(_ != null) def jedit_text_areas(view: View): Iterator[JEditTextArea] = if (view == null) Iterator.empty else view.getEditPanes().iterator.filter(_ != null).map(_.getTextArea).filter(_ != null) def jedit_text_areas(): Iterator[JEditTextArea] = jedit_views().flatMap(jedit_text_areas) def jedit_text_areas(buffer: JEditBuffer): Iterator[JEditTextArea] = jedit_text_areas().filter(_.getBuffer == buffer) def buffer_lock[A](buffer: JEditBuffer)(body: => A): A = { try { buffer.readLock(); body } finally { buffer.readUnlock() } } def buffer_edit[A](buffer: JEditBuffer)(body: => A): A = { try { buffer.beginCompoundEdit(); body } finally { buffer.endCompoundEdit() } } /* get text */ def get_text(buffer: JEditBuffer, range: Text.Range): Option[String] = try { Some(buffer.getText(range.start, range.length)) } catch { case _: ArrayIndexOutOfBoundsException => None } /* point range */ def point_range(buffer: JEditBuffer, offset: Text.Offset): Text.Range = if (offset < 0) Text.Range.offside else buffer_lock(buffer) { def text(i: Text.Offset): Char = buffer.getText(i, 1).charAt(0) try { val c = text(offset) if (Character.isHighSurrogate(c) && Character.isLowSurrogate(text(offset + 1))) Text.Range(offset, offset + 2) else if (Character.isLowSurrogate(c) && Character.isHighSurrogate(text(offset - 1))) Text.Range(offset - 1, offset + 1) else Text.Range(offset, offset + 1) } catch { case _: ArrayIndexOutOfBoundsException => Text.Range(offset, offset + 1) } } /* text ranges */ def buffer_range(buffer: JEditBuffer): Text.Range = Text.Range(0, buffer.getLength) def line_range(buffer: JEditBuffer, line: Int): Text.Range = Text.Range(buffer.getLineStartOffset(line), buffer.getLineEndOffset(line) min buffer.getLength) def caret_range(text_area: TextArea): Text.Range = point_range(text_area.getBuffer, text_area.getCaretPosition) def visible_range(text_area: TextArea): Option[Text.Range] = { val buffer = text_area.getBuffer val n = text_area.getVisibleLines if (n > 0) { val start = text_area.getScreenLineStartOffset(0) val raw_end = text_area.getScreenLineEndOffset(n - 1) val end = if (raw_end >= 0) raw_end min buffer.getLength else buffer.getLength Some(Text.Range(start, end)) } else None } def invalidate_range(text_area: TextArea, range: Text.Range): Unit = { val buffer = text_area.getBuffer buffer_range(buffer).try_restrict(range) match { case Some(range1) if !range1.is_singularity => try { text_area.invalidateLineRange( buffer.getLineOfOffset(range1.start), buffer.getLineOfOffset(range1.stop)) } catch { case _: ArrayIndexOutOfBoundsException => } case _ => } } def invalidate(text_area: TextArea): Unit = { val visible_lines = text_area.getVisibleLines if (visible_lines > 0) text_area.invalidateScreenLineRange(0, visible_lines) } /* graphics range */ case class Gfx_Range(x: Int, y: Int, length: Int) // NB: jEdit always normalizes \r\n and \r to \n // NB: last line lacks \n def gfx_range(text_area: TextArea, range: Text.Range): Option[Gfx_Range] = { val metric = pretty_metric(text_area.getPainter) val char_width = (metric.unit * metric.average).round.toInt val buffer = text_area.getBuffer val end = buffer.getLength val stop = range.stop val (p, q, r) = try { val p = text_area.offsetToXY(range.start) val (q, r) = if (get_text(buffer, Text.Range(stop - 1, stop)) == Some("\n")) (text_area.offsetToXY(stop - 1), char_width) else if (stop >= end) (text_area.offsetToXY(end), char_width * (stop - end)) else (text_area.offsetToXY(stop), 0) (p, q, r) } catch { case _: ArrayIndexOutOfBoundsException => (null, null, 0) } if (p != null && q != null && p.x < q.x + r && p.y == q.y) Some(Gfx_Range(p.x, p.y, q.x + r - p.x)) else None } /* pixel range */ def pixel_range(text_area: TextArea, x: Int, y: Int): Option[Text.Range] = { // coordinates wrt. inner painter component val painter = text_area.getPainter if (0 <= x && x < painter.getWidth && 0 <= y && y < painter.getHeight) { val offset = text_area.xyToOffset(x, y, false) if (offset >= 0) { val range = point_range(text_area.getBuffer, offset) gfx_range(text_area, range) match { case Some(g) if g.x <= x && x < g.x + g.length => Some(range) case _ => None } } else None } else None } /* pretty text metric */ abstract class Pretty_Metric extends Pretty.Metric { def average: Double } def pretty_metric(painter: TextAreaPainter): Pretty_Metric = new Pretty_Metric { def string_width(s: String): Double = painter.getFont.getStringBounds(s, painter.getFontRenderContext).getWidth val unit: Double = string_width(Symbol.space) max 1.0 val average: Double = string_width("mix") / (3 * unit) def apply(s: String): Double = if (s == "\n") 1.0 else string_width(s) / unit } /* icons */ def load_icon(name: String): Icon = { val name1 = if (name.startsWith("idea-icons/")) { val file = Path.explode("$ISABELLE_IDEA_ICONS").file.toURI.toASCIIString "jar:" + file + "!/" + name } else name val icon = GUIUtilities.loadIcon(name1) if (icon.getIconWidth < 0 || icon.getIconHeight < 0) error("Bad icon: " + name) else icon } def load_image_icon(name: String): ImageIcon = load_icon(name) match { case icon: ImageIcon => icon case _ => error("Bad image icon: " + name) } /* key event handling */ def request_focus_view(alt_view: View = null): Unit = - isabelle.jedit_base.JEdit_Lib.request_focus_view(alt_view) + { + val view = if (alt_view != null) alt_view else jEdit.getActiveView() + if (view != null) { + val text_area = view.getTextArea + if (text_area != null) text_area.requestFocus() + } + } def propagate_key(view: View, evt: KeyEvent): Unit = { if (view != null && !evt.isConsumed) view.getInputHandler().processKeyEvent(evt, View.ACTION_BAR, false) } def key_listener( key_typed: KeyEvent => Unit = _ => (), key_pressed: KeyEvent => Unit = _ => (), key_released: KeyEvent => Unit = _ => ()): KeyListener = { def process_key_event(evt0: KeyEvent, handle: KeyEvent => Unit): Unit = { val evt = KeyEventWorkaround.processKeyEvent(evt0) if (evt != null) handle(evt) } new KeyListener { def keyTyped(evt: KeyEvent): Unit = process_key_event(evt, key_typed) def keyPressed(evt: KeyEvent): Unit = process_key_event(evt, key_pressed) def keyReleased(evt: KeyEvent): Unit = process_key_event(evt, key_released) } } def special_key(evt: KeyEvent): Boolean = { // cf. 5.2.0/jEdit/org/gjt/sp/jedit/gui/KeyEventWorkaround.java val mod = evt.getModifiersEx (mod & InputEvent.CTRL_DOWN_MASK) != 0 && (mod & InputEvent.ALT_DOWN_MASK) == 0 || (mod & InputEvent.CTRL_DOWN_MASK) == 0 && (mod & InputEvent.ALT_DOWN_MASK) != 0 && !Debug.ALT_KEY_PRESSED_DISABLED || (mod & InputEvent.META_DOWN_MASK) != 0 } def command_modifier(evt: InputEvent): Boolean = (evt.getModifiersEx & Toolkit.getDefaultToolkit.getMenuShortcutKeyMaskEx) != 0 def shift_modifier(evt: InputEvent): Boolean = (evt.getModifiersEx & InputEvent.SHIFT_DOWN_MASK) != 0 def modifier_string(evt: InputEvent): String = KeyEventTranslator.getModifierString(evt) match { case null => "" case s => s } } diff --git a/src/Pure/Tools/main.scala b/src/Tools/jEdit/src/main.scala rename from src/Pure/Tools/main.scala rename to src/Tools/jEdit/src/main.scala --- a/src/Pure/Tools/main.scala +++ b/src/Tools/jEdit/src/main.scala @@ -1,151 +1,146 @@ -/* Title: Pure/Tools/main.scala +/* Title: src/Tools/jEdit/src/main.scala Author: Makarius Main Isabelle application entry point. */ -package isabelle +package isabelle.jedit -import java.lang.{Class, ClassLoader} +import isabelle._ + +import org.gjt.sp.jedit.{MiscUtilities, jEdit} object Main { /* main entry point */ def main(args: Array[String]): Unit = { if (args.nonEmpty && args(0) == "-init") { Isabelle_System.init() } else { val start = { try { Isabelle_System.init() Isabelle_Fonts.init() GUI.init_lafs() /* ROOTS template */ { val roots = Path.explode("$ISABELLE_HOME_USER/ROOTS") if (!roots.is_file) File.write(roots, """# Additional session root directories # # * each line contains one directory entry in Isabelle path notation, e.g. # # $ISABELLE_HOME/../AFP/thys # # for a copy of AFP put side-by-side to the Isabelle distribution # # * Isabelle/jEdit provides formal markup for C-hover-click and completion # # * lines starting with "#" are stripped # # * changes require restart of the Isabelle application # #:mode=text:encoding=UTF-8: #$ISABELLE_HOME/../AFP/thys """) } /* settings directory */ val settings_dir = Path.explode("$JEDIT_SETTINGS") val properties = settings_dir + Path.explode("properties") if (properties.is_file) { val props1 = split_lines(File.read(properties)) - val props2 = props1.filterNot(_.startsWith("plugin-blacklist.Isabelle-jEdit")) + val props2 = props1.filterNot(_.startsWith("plugin-blacklist.isabelle_jedit")) if (props1 != props2) File.write(properties, cat_lines(props2)) } Isabelle_System.make_directory(settings_dir + Path.explode("DockableWindowManager")) if (!(settings_dir + Path.explode("perspective.xml")).is_file) { File.write(settings_dir + Path.explode("DockableWindowManager/perspective-view0.xml"), """""") File.write(settings_dir + Path.explode("perspective.xml"), XML.header + """ """) } + for (plugin <- List("jedit_base", "jedit_main")) { + val dir = Path.explode("$ISABELLE_HOME/src/Tools/jEdit") + Path.basic(plugin) + val context = isabelle.setup.Build.directory_context(dir.java_path) + isabelle.setup.Build.build(context, false) + } + /* args */ val jedit_settings = "-settings=" + File.platform_path(Path.explode("$JEDIT_SETTINGS")) val jedit_server = System.getProperty("isabelle.jedit_server") match { case null | "" => "-noserver" case name => "-server=" + name } val jedit_options = Isabelle_System.getenv_strict("JEDIT_OPTIONS").split(" +") val more_args = { args.toList.dropWhile(arg => arg.startsWith("-") && arg != "--") match { case Nil | List("--") => args ++ Array(File.platform_path(Path.explode("$USER_HOME/Scratch.thy"))) case List(":") => args.slice(0, args.size - 1) case _ => args } } /* environment */ - def putenv(name: String, value: String): Unit = - { - val misc = - Class.forName("org.gjt.sp.jedit.MiscUtilities", true, ClassLoader.getSystemClassLoader) - val putenv = misc.getMethod("putenv", classOf[String], classOf[String]) - putenv.invoke(null, name, value) + for (name <- List("ISABELLE_HOME", "ISABELLE_HOME_USER", "JEDIT_HOME", "JEDIT_SETTINGS")) { + MiscUtilities.putenv(name, File.platform_path(Isabelle_System.getenv(name))) } - - for (name <- List("ISABELLE_HOME", "ISABELLE_HOME_USER", "JEDIT_HOME", "JEDIT_SETTINGS")) { - putenv(name, File.platform_path(Isabelle_System.getenv(name))) - } - putenv("ISABELLE_ROOT", null) + MiscUtilities.putenv("ISABELLE_ROOT", null) /* properties */ - System.setProperty("jedit.home", File.platform_path(Path.explode("$JEDIT_HOME/dist"))) + System.setProperty("jedit.home", File.platform_path(Path.explode("$JEDIT_HOME"))) System.setProperty("scala.home", File.platform_path(Path.explode("$SCALA_HOME"))) System.setProperty("scala.color", "false") /* main startup */ - val jedit = - Class.forName("org.gjt.sp.jedit.jEdit", true, ClassLoader.getSystemClassLoader) - val jedit_main = jedit.getMethod("main", classOf[Array[String]]) - - () => jedit_main.invoke( - null, Array(jedit_settings, jedit_server) ++ jedit_options ++ more_args) + () => jEdit.main(Array(jedit_settings, jedit_server) ++ jedit_options ++ more_args) } catch { case exn: Throwable => GUI.init_laf() GUI.dialog(null, "Isabelle", GUI.scrollable_text(Exn.message(exn))) sys.exit(2) } } start() } } } diff --git a/src/Tools/jEdit/src/plugin.scala b/src/Tools/jEdit/src/main_plugin.scala rename from src/Tools/jEdit/src/plugin.scala rename to src/Tools/jEdit/src/main_plugin.scala --- a/src/Tools/jEdit/src/plugin.scala +++ b/src/Tools/jEdit/src/main_plugin.scala @@ -1,499 +1,501 @@ -/* Title: Tools/jEdit/src/plugin.scala +/* Title: Tools/jEdit/src/main_plugin.scala Author: Makarius Main plumbing for PIDE infrastructure as jEdit plugin. */ package isabelle.jedit import isabelle._ import javax.swing.JOptionPane import java.io.{File => JFile} import org.gjt.sp.jedit.{jEdit, EBMessage, EBPlugin, Buffer, View, PerspectiveManager} import org.gjt.sp.jedit.textarea.JEditTextArea import org.gjt.sp.jedit.syntax.ModeProvider import org.gjt.sp.jedit.msg.{EditorStarted, BufferUpdate, EditPaneUpdate, PropertiesChanged, ViewUpdate} import org.gjt.sp.util.SyntaxUtilities import org.gjt.sp.util.Log object PIDE { /* semantic document content */ def maybe_snapshot(view: View = null): Option[Document.Snapshot] = GUI_Thread.now { val buffer = JEdit_Lib.jedit_view(view).getBuffer Document_Model.get(buffer).map(_.snapshot()) } def maybe_rendering(view: View = null): Option[JEdit_Rendering] = GUI_Thread.now { val text_area = JEdit_Lib.jedit_view(view).getTextArea Document_View.get(text_area).map(_.get_rendering()) } def snapshot(view: View = null): Document.Snapshot = maybe_snapshot(view) getOrElse error("No document model for current buffer") def rendering(view: View = null): JEdit_Rendering = maybe_rendering(view) getOrElse error("No document view for current text area") /* plugin instance */ - @volatile var _plugin: Plugin = null + @volatile var _plugin: Main_Plugin = null - def plugin: Plugin = + def plugin: Main_Plugin = if (_plugin == null) error("Uninitialized Isabelle/jEdit plugin") else _plugin def options: JEdit_Options = plugin.options def resources: JEdit_Resources = plugin.resources def session: Session = plugin.session object editor extends JEdit_Editor } -class Plugin extends EBPlugin +class Main_Plugin extends EBPlugin { /* options */ private var _options: JEdit_Options = null private def init_options(): Unit = _options = new JEdit_Options(Options.init()) def options: JEdit_Options = _options /* resources */ private var _resources: JEdit_Resources = null private def init_resources(): Unit = _resources = JEdit_Resources(options.value) def resources: JEdit_Resources = _resources /* session */ private var _session: Session = null private def init_session(): Unit = _session = new Session(options.value, resources) def session: Session = _session /* misc support */ val completion_history = new Completion.History_Variable val spell_checker = new Spell_Checker_Variable /* global changes */ def options_changed(): Unit = { session.global_options.post(Session.Global_Options(options.value)) delay_load.invoke() } def deps_changed(): Unit = { delay_load.invoke() } /* theory files */ lazy val delay_init = Delay.last(options.seconds("editor_load_delay"), gui = true) { init_models() } private val delay_load_active = Synchronized(false) private def delay_load_activated(): Boolean = delay_load_active.guarded_access(a => Some((!a, true))) private def delay_load_action(): Unit = { if (Isabelle.continuous_checking && delay_load_activated() && PerspectiveManager.isPerspectiveEnabled) { if (JEdit_Lib.jedit_buffers().exists(_.isLoading)) delay_load.invoke() else { val required_files = { val models = Document_Model.get_models() val thys = (for ((node_name, model) <- models.iterator if model.is_theory) yield (node_name, Position.none)).toList val thy_files1 = resources.dependencies(thys).theories val thy_files2 = (for { (name, _) <- models.iterator thy_name <- resources.make_theory_name(name) } yield thy_name).toList val aux_files = if (options.bool("jedit_auto_resolve")) { val stable_tip_version = if (models.forall(p => p._2.is_stable)) session.get_state().stable_tip_version else None stable_tip_version match { case Some(version) => resources.undefined_blobs(version.nodes) case None => delay_load.invoke(); Nil } } else Nil (thy_files1 ::: thy_files2 ::: aux_files).filterNot(models.isDefinedAt) } if (required_files.nonEmpty) { try { Isabelle_Thread.fork(name = "resolve_dependencies") { val loaded_files = for { name <- required_files text <- resources.read_file_content(name) } yield (name, text) GUI_Thread.later { try { Document_Model.provide_files(session, loaded_files) delay_init.invoke() } finally { delay_load_active.change(_ => false) } } } } catch { case _: Throwable => delay_load_active.change(_ => false) } } else delay_load_active.change(_ => false) } } } private lazy val delay_load = Delay.last(options.seconds("editor_load_delay"), gui = true) { delay_load_action() } private def file_watcher_action(changed: Set[JFile]): Unit = if (Document_Model.sync_files(changed)) PIDE.editor.invoke_generated() lazy val file_watcher: File_Watcher = File_Watcher(file_watcher_action, options.seconds("editor_load_delay")) /* session phase */ val session_phase_changed: Session.Consumer[Session.Phase] = Session.Consumer("Isabelle/jEdit") { case Session.Terminated(result) if !result.ok => GUI_Thread.later { GUI.error_dialog(jEdit.getActiveView, "Prover process terminated with error", "Isabelle Syslog", GUI.scrollable_text(session.syslog_content())) } case Session.Ready if !shutting_down.value => init_models() if (!Isabelle.continuous_checking) { GUI_Thread.later { val answer = GUI.confirm_dialog(jEdit.getActiveView, "Continuous checking of PIDE document", JOptionPane.YES_NO_OPTION, "Continuous checking is presently disabled:", "editor buffers will remain inactive!", "Enable continuous checking now?") if (answer == 0) Isabelle.continuous_checking = true } } delay_load.invoke() case Session.Shutdown => GUI_Thread.later { delay_load.revoke() delay_init.revoke() PIDE.editor.shutdown() exit_models(JEdit_Lib.jedit_buffers().toList) } case _ => } /* document model and view */ def exit_models(buffers: List[Buffer]): Unit = { GUI_Thread.now { buffers.foreach(buffer => JEdit_Lib.buffer_lock(buffer) { JEdit_Lib.jedit_text_areas(buffer).foreach(Document_View.exit) Document_Model.exit(buffer) }) } } def init_models(): Unit = { GUI_Thread.now { PIDE.editor.flush() for { buffer <- JEdit_Lib.jedit_buffers() if buffer != null && !buffer.getBooleanProperty(Buffer.GZIPPED) } { if (buffer.isLoaded) { JEdit_Lib.buffer_lock(buffer) { val node_name = resources.node_name(buffer) val model = Document_Model.init(session, node_name, buffer) for { text_area <- JEdit_Lib.jedit_text_areas(buffer) if Document_View.get(text_area).map(_.model) != Some(model) } Document_View.init(model, text_area) } } else delay_init.invoke() } PIDE.editor.invoke_generated() } } def init_view(buffer: Buffer, text_area: JEditTextArea): Unit = GUI_Thread.now { JEdit_Lib.buffer_lock(buffer) { Document_Model.get(buffer) match { case Some(model) => Document_View.init(model, text_area) case None => } } } def exit_view(buffer: Buffer, text_area: JEditTextArea): Unit = GUI_Thread.now { JEdit_Lib.buffer_lock(buffer) { Document_View.exit(text_area) } } /* main plugin plumbing */ @volatile private var startup_failure: Option[Throwable] = None @volatile private var startup_notified = false private def init_editor(view: View): Unit = { Keymap_Merge.check_dialog(view) Session_Build.check_dialog(view) } private def init_title(view: View): Unit = { val title = proper_string(Isabelle_System.getenv("ISABELLE_IDENTIFIER")).getOrElse("Isabelle") + "/" + PIDE.resources.session_name val marker = "\u200B" val old_title = view.getViewConfig.title if (old_title == null || old_title.startsWith(marker)) { view.setUserTitle(marker + title) } } override def handleMessage(message: EBMessage): Unit = { GUI_Thread.assert {} if (startup_failure.isDefined && !startup_notified) { message match { case msg: EditorStarted => GUI.error_dialog(null, "Isabelle plugin startup failure", GUI.scrollable_text(Exn.message(startup_failure.get)), "Prover IDE inactive!") startup_notified = true case _ => } } if (startup_failure.isEmpty) { message match { case msg: EditorStarted => if (resources.session_errors.nonEmpty) { GUI.warning_dialog(jEdit.getActiveView, "Bad session structure: may cause problems with theory imports", GUI.scrollable_text(cat_lines(resources.session_errors))) } jEdit.propertiesChanged() val view = jEdit.getActiveView() init_editor(view) PIDE.editor.hyperlink_position(true, Document.Snapshot.init, JEdit_Sessions.logic_root(options.value)).foreach(_.follow(view)) case msg: ViewUpdate if msg.getWhat == ViewUpdate.CREATED && msg.getView != null => init_title(msg.getView) case msg: BufferUpdate if msg.getWhat == BufferUpdate.LOAD_STARTED || msg.getWhat == BufferUpdate.CLOSING => if (msg.getBuffer != null) { exit_models(List(msg.getBuffer)) PIDE.editor.invoke_generated() } case msg: BufferUpdate if msg.getWhat == BufferUpdate.PROPERTIES_CHANGED || msg.getWhat == BufferUpdate.LOADED => if (session.is_ready) { delay_init.invoke() delay_load.invoke() } case msg: EditPaneUpdate if msg.getWhat == EditPaneUpdate.BUFFER_CHANGING || msg.getWhat == EditPaneUpdate.BUFFER_CHANGED || msg.getWhat == EditPaneUpdate.CREATED || msg.getWhat == EditPaneUpdate.DESTROYED => val edit_pane = msg.getEditPane val buffer = edit_pane.getBuffer val text_area = edit_pane.getTextArea if (buffer != null && text_area != null) { if (msg.getWhat == EditPaneUpdate.BUFFER_CHANGED || msg.getWhat == EditPaneUpdate.CREATED) { if (session.is_ready) init_view(buffer, text_area) } else { Isabelle.dismissed_popups(text_area.getView) exit_view(buffer, text_area) } if (msg.getWhat == EditPaneUpdate.CREATED) Completion_Popup.Text_Area.init(text_area) if (msg.getWhat == EditPaneUpdate.DESTROYED) Completion_Popup.Text_Area.exit(text_area) } case msg: PropertiesChanged => for { view <- JEdit_Lib.jedit_views() edit_pane <- JEdit_Lib.jedit_edit_panes(view) } { val buffer = edit_pane.getBuffer val text_area = edit_pane.getTextArea if (buffer != null && text_area != null) init_view(buffer, text_area) } spell_checker.update(options.value) session.update_options(options.value) case _ => } } } /* mode provider */ private var orig_mode_provider: ModeProvider = null private var pide_mode_provider: ModeProvider = null def init_mode_provider(): Unit = { orig_mode_provider = ModeProvider.instance if (orig_mode_provider.isInstanceOf[ModeProvider]) { pide_mode_provider = new Token_Markup.Mode_Provider(orig_mode_provider) ModeProvider.instance = pide_mode_provider } } def exit_mode_provider(): Unit = { if (ModeProvider.instance == pide_mode_provider) ModeProvider.instance = orig_mode_provider } /* HTTP server */ val http_root: String = "/" + UUID.random() val http_server: HTTP.Server = HTTP.server(Document_Model.http_handlers(http_root)) /* start and stop */ private val shutting_down = Synchronized(false) override def start(): Unit = { /* strict initialization */ init_options() init_resources() init_session() PIDE._plugin = this /* non-strict initialization */ try { completion_history.load() spell_checker.update(options.value) JEdit_Lib.jedit_views().foreach(init_title) - isabelle.jedit_base.Syntax_Style.set_style_extender(Syntax_Style.Extender) + Syntax_Style.set_extender(Syntax_Style.Extender) init_mode_provider() JEdit_Lib.jedit_text_areas().foreach(Completion_Popup.Text_Area.init) http_server.start() startup_failure = None } catch { case exn: Throwable => startup_failure = Some(exn) startup_notified = false Log.log(Log.ERROR, this, exn) } shutting_down.change(_ => false) val view = jEdit.getActiveView() if (view != null) init_editor(view) } override def stop(): Unit = { http_server.stop() - isabelle.jedit_base.Syntax_Style.dummy_style_extender() + Syntax_Style.set_extender(Syntax_Style.Base_Extender) + exit_mode_provider() JEdit_Lib.jedit_text_areas().foreach(Completion_Popup.Text_Area.exit) if (startup_failure.isEmpty) { options.value.save_prefs() completion_history.value.save() } exit_models(JEdit_Lib.jedit_buffers().toList) shutting_down.change(_ => true) session.stop() file_watcher.shutdown() PIDE.editor.shutdown() + + PIDE._plugin = null } } - diff --git a/src/Tools/jEdit/src/monitor_dockable.scala b/src/Tools/jEdit/src/monitor_dockable.scala --- a/src/Tools/jEdit/src/monitor_dockable.scala +++ b/src/Tools/jEdit/src/monitor_dockable.scala @@ -1,145 +1,144 @@ /* Title: Tools/jEdit/src/monitor_dockable.scala Author: Makarius Monitor for runtime statistics. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.BorderLayout import scala.collection.immutable.Queue import scala.swing.{TextField, ComboBox, Button} import scala.swing.event.{SelectionChanged, ButtonClicked, ValueChanged} import org.jfree.chart.ChartPanel import org.jfree.data.xy.XYSeriesCollection import org.gjt.sp.jedit.View class Monitor_Dockable(view: View, position: String) extends Dockable(view, position) { /* chart data -- owned by GUI thread */ private var statistics = Queue.empty[Properties.T] private var statistics_length = 0 private def add_statistics(stats: Properties.T): Unit = { statistics = statistics.enqueue(stats) statistics_length += 1 limit_data.text match { case Value.Int(limit) => while (statistics_length > limit) { statistics = statistics.dequeue._2 statistics_length -= 1 } case _ => } } private def clear_statistics(): Unit = { statistics = Queue.empty statistics_length = 0 } private var data_name = ML_Statistics.all_fields.head._1 private val chart = ML_Statistics.empty.chart(null, Nil) private val data = chart.getXYPlot.getDataset.asInstanceOf[XYSeriesCollection] private def update_chart(): Unit = { ML_Statistics.all_fields.find(_._1 == data_name) match { case None => case Some((_, fields)) => ML_Statistics(statistics.toList).update_data(data, fields) } } private val input_delay = Delay.first(PIDE.options.seconds("editor_input_delay"), gui = true) { update_chart() } private val update_delay = Delay.first(PIDE.options.seconds("editor_chart_delay"), gui = true) { update_chart() } /* controls */ private val select_data = new ComboBox[String](ML_Statistics.all_fields.map(_._1)) { tooltip = "Select visualized data collection" listenTo(selection) reactions += { case SelectionChanged(_) => data_name = selection.item update_chart() } } private val limit_data = new TextField("200", 5) { tooltip = "Limit for accumulated data" verifier = { case Value.Int(x) => x > 0 case _ => false } reactions += { case ValueChanged(_) => input_delay.invoke() } } private val reset_data = new Button("Reset") { tooltip = "Reset accumulated data" reactions += { case ButtonClicked(_) => clear_statistics() update_chart() } } private val full_gc = new Button("GC") { tooltip = "Full garbage collection of ML heap" reactions += { case ButtonClicked(_) => PIDE.session.protocol_command("ML_Heap.full_gc") } } private val share_common_data = new Button("Sharing") { tooltip = "Share common data of ML heap" reactions += { case ButtonClicked(_) => PIDE.session.protocol_command("ML_Heap.share_common_data") } } private val controls = Wrap_Panel(List(select_data, limit_data, reset_data, full_gc, share_common_data)) /* layout */ set_content(new ChartPanel(chart)) add(controls.peer, BorderLayout.NORTH) /* main */ private val main = Session.Consumer[Session.Runtime_Statistics](getClass.getName) { stats => add_statistics(stats.props) update_delay.invoke() } override def init(): Unit = { PIDE.session.runtime_statistics += main } override def exit(): Unit = { PIDE.session.runtime_statistics -= main } } diff --git a/src/Tools/jEdit/src/output_dockable.scala b/src/Tools/jEdit/src/output_dockable.scala --- a/src/Tools/jEdit/src/output_dockable.scala +++ b/src/Tools/jEdit/src/output_dockable.scala @@ -1,160 +1,159 @@ /* Title: Tools/jEdit/src/output_dockable.scala Author: Makarius Dockable window with result message output. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Button, CheckBox} import scala.swing.event.ButtonClicked import java.awt.BorderLayout import java.awt.event.{ComponentEvent, ComponentAdapter} import org.gjt.sp.jedit.View class Output_Dockable(view: View, position: String) extends Dockable(view, position) { /* component state -- owned by GUI thread */ private var do_update = true private var current_output: List[XML.Tree] = Nil /* pretty text area */ val pretty_text_area = new Pretty_Text_Area(view) set_content(pretty_text_area) override def detach_operation: Option[() => Unit] = pretty_text_area.detach_operation private def handle_resize(): Unit = { GUI_Thread.require {} pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } private def handle_update(follow: Boolean, restriction: Option[Set[Command]]): Unit = { GUI_Thread.require {} for { snapshot <- PIDE.editor.current_node_snapshot(view) if follow && !snapshot.is_outdated } { val (command, results) = PIDE.editor.current_command(view, snapshot) match { case Some(command) => (command, snapshot.command_results(command)) case None => (Command.empty, Command.Results.empty) } val new_output = if (restriction.isEmpty || restriction.get.contains(command)) Rendering.output_messages(results) else current_output if (current_output != new_output) { pretty_text_area.update(snapshot, results, Pretty.separate(new_output)) current_output = new_output } } } /* controls */ private def output_state: Boolean = PIDE.options.bool("editor_output_state") private def output_state_=(b: Boolean): Unit = { if (output_state != b) { PIDE.options.bool("editor_output_state") = b PIDE.session.update_options(PIDE.options.value) PIDE.editor.flush_edits(hidden = true) PIDE.editor.flush() } } private val output_state_button = new CheckBox("Proof state") { tooltip = "Output of proof state (normally shown on State panel)" reactions += { case ButtonClicked(_) => output_state = selected } selected = output_state } private val auto_update_button = new CheckBox("Auto update") { tooltip = "Indicate automatic update following cursor movement" reactions += { case ButtonClicked(_) => do_update = this.selected; handle_update(do_update, None) } selected = do_update } private val update_button = new Button("Update") { tooltip = "Update display according to the command at cursor position" reactions += { case ButtonClicked(_) => handle_update(true, None) } } private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private val controls = Wrap_Panel( List(output_state_button, auto_update_button, update_button, pretty_text_area.search_label, pretty_text_area.search_field, zoom)) add(controls.peer, BorderLayout.NORTH) /* main */ private val main = Session.Consumer[Any](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() output_state_button.selected = output_state handle_update(do_update, None) } case changed: Session.Commands_Changed => val restriction = if (changed.assignment) None else Some(changed.commands) GUI_Thread.later { handle_update(do_update, restriction) } case Session.Caret_Focus => GUI_Thread.later { handle_update(do_update, None) } } override def init(): Unit = { PIDE.session.global_options += main PIDE.session.commands_changed += main PIDE.session.caret_focus += main handle_update(true, None) } override def exit(): Unit = { PIDE.session.global_options -= main PIDE.session.commands_changed -= main PIDE.session.caret_focus -= main delay_resize.revoke() } /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) } diff --git a/src/Tools/jEdit/src-base/pide_docking_framework.scala b/src/Tools/jEdit/src/pide_docking_framework.scala rename from src/Tools/jEdit/src-base/pide_docking_framework.scala rename to src/Tools/jEdit/src/pide_docking_framework.scala --- a/src/Tools/jEdit/src-base/pide_docking_framework.scala +++ b/src/Tools/jEdit/src/pide_docking_framework.scala @@ -1,72 +1,72 @@ -/* Title: Tools/jEdit/src-base/pide_docking_framework.scala +/* Title: Tools/jEdit/src/pide_docking_framework.scala Author: Makarius PIDE docking framework: "Original" with some add-ons. */ -package isabelle.jedit_base +package isabelle.jedit import isabelle._ import java.util.{List => JList} import java.awt.event.{ActionListener, ActionEvent} import javax.swing.{JPopupMenu, JMenuItem} import org.gjt.sp.jedit.View import org.gjt.sp.jedit.gui.{DockableWindowManagerProvider, DockableWindowFactory, DockableWindowManager, DockableWindowManagerImpl, DockableWindowContainer, FloatingWindowContainer, PanelWindowContainer} class PIDE_Docking_Framework extends DockableWindowManagerProvider { override def create( view: View, instance: DockableWindowFactory, config: View.ViewConfig): DockableWindowManager = new DockableWindowManagerImpl(view, instance, config) { override def createPopupMenu( container: DockableWindowContainer, dockable_name: String, clone: Boolean): JPopupMenu = { val menu = super.createPopupMenu(container, dockable_name, clone) val detach_operation: Option[() => Unit] = container match { case floating: FloatingWindowContainer => Untyped.get[AnyRef](Untyped.get[AnyRef](floating, "entry"), "win") match { case dockable: Dockable => dockable.detach_operation case _ => None } case panel: PanelWindowContainer => val entries = Untyped.get[JList[AnyRef]](panel, "dockables").toArray val wins = (for { entry <- entries.iterator if Untyped.get[String](Untyped.get(entry, "factory"), "name") == dockable_name win = Untyped.get[Any](entry, "win") if win != null } yield win).toList wins match { case List(dockable: Dockable) => dockable.detach_operation case _ => None } case _ => None } if (detach_operation.isDefined) { val detach_item = new JMenuItem("Detach") detach_item.addActionListener(new ActionListener { def actionPerformed(evt: ActionEvent): Unit = detach_operation.get.apply() }) menu.add(detach_item) } menu } } } diff --git a/src/Tools/jEdit/src/protocol_dockable.scala b/src/Tools/jEdit/src/protocol_dockable.scala --- a/src/Tools/jEdit/src/protocol_dockable.scala +++ b/src/Tools/jEdit/src/protocol_dockable.scala @@ -1,52 +1,51 @@ /* Title: Tools/jEdit/src/protocol_dockable.scala Author: Makarius Dockable window for protocol messages. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.BorderLayout import scala.swing.{TextArea, ScrollPane} import org.gjt.sp.jedit.View class Protocol_Dockable(view: View, position: String) extends Dockable(view, position) { /* text area */ private val text_area = new TextArea /* layout */ set_content(new ScrollPane(text_area)) /* main */ private val main = Session.Consumer[Prover.Message](getClass.getName) { case input: Prover.Input => GUI_Thread.later { text_area.append(input.toString + "\n\n") } case output: Prover.Output => GUI_Thread.later { text_area.append(output.message.toString + "\n\n") } } override def init(): Unit = { PIDE.session.all_messages += main } override def exit(): Unit = { PIDE.session.all_messages -= main } } diff --git a/src/Tools/jEdit/src/query_dockable.scala b/src/Tools/jEdit/src/query_dockable.scala --- a/src/Tools/jEdit/src/query_dockable.scala +++ b/src/Tools/jEdit/src/query_dockable.scala @@ -1,353 +1,352 @@ /* Title: Tools/jEdit/src/query_dockable.scala Author: Makarius Dockable window for query operations. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent} import javax.swing.{JComponent, JTextField} import scala.swing.{Button, Component, TextField, CheckBox, Label, ListView, ComboBox, TabbedPane, BorderPanel} import scala.swing.event.{SelectionChanged, ButtonClicked, Key, KeyPressed} import org.gjt.sp.jedit.View object Query_Dockable { private abstract class Operation(view: View) { val pretty_text_area = new Pretty_Text_Area(view) def query_operation: Query_Operation[View] def query: JComponent def select: Unit def page: TabbedPane.Page } } class Query_Dockable(view: View, position: String) extends Dockable(view, position) { /* common GUI components */ private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private def make_query(property: String, tooltip: String, apply_query: () => Unit) : Completion_Popup.History_Text_Field = new Completion_Popup.History_Text_Field(property) { override def processKeyEvent(evt: KeyEvent): Unit = { if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) apply_query() super.processKeyEvent(evt) } { val max = getPreferredSize; max.width = Integer.MAX_VALUE; setMaximumSize(max) } setColumns(40) setToolTipText(tooltip) setFont(GUI.imitate_font(getFont, scale = 1.2)) } /* consume status */ def consume_status( process_indicator: Process_Indicator, status: Query_Operation.Status.Value): Unit = { status match { case Query_Operation.Status.WAITING => process_indicator.update("Waiting for evaluation of context ...", 5) case Query_Operation.Status.RUNNING => process_indicator.update("Running find operation ...", 15) case Query_Operation.Status.FINISHED => process_indicator.update(null, 0) } } /* find theorems */ private val find_theorems = new Query_Dockable.Operation(view) { /* query */ private val process_indicator = new Process_Indicator val query_operation = new Query_Operation(PIDE.editor, view, "find_theorems", consume_status(process_indicator, _), (snapshot, results, body) => pretty_text_area.update(snapshot, results, Pretty.separate(body))) private def apply_query(): Unit = { query.addCurrentToHistory() query_operation.apply_query(List(limit.text, allow_dups.selected.toString, query.getText)) } private val query_label = new Label("Find:") { tooltip = GUI.tooltip_lines( "Search criteria for find operation, e.g.\n\"_ = _\" \"(+)\" name: Group -name: monoid") } val query = make_query("isabelle-find-theorems", query_label.tooltip, apply_query _) /* GUI page */ private val limit = new TextField(PIDE.options.int("find_theorems_limit").toString, 5) { tooltip = "Limit of displayed results" verifier = (s: String) => s match { case Value.Int(x) => x >= 0 case _ => false } listenTo(keys) reactions += { case KeyPressed(_, Key.Enter, 0, _) => apply_query() } } private val allow_dups = new CheckBox("Duplicates") { tooltip = "Show all versions of matching theorems" selected = false reactions += { case ButtonClicked(_) => apply_query() } } private val apply_button = new Button("Apply") { tooltip = "Find theorems meeting specified criteria" reactions += { case ButtonClicked(_) => apply_query() } } private val control_panel = Wrap_Panel( List(query_label, Component.wrap(query), limit, allow_dups, process_indicator.component, apply_button, pretty_text_area.search_label, pretty_text_area.search_field)) def select: Unit = { control_panel.contents += zoom } val page = new TabbedPane.Page("Find Theorems", new BorderPanel { layout(control_panel) = BorderPanel.Position.North layout(Component.wrap(pretty_text_area)) = BorderPanel.Position.Center }, apply_button.tooltip) } /* find consts */ private val find_consts = new Query_Dockable.Operation(view) { /* query */ private val process_indicator = new Process_Indicator val query_operation = new Query_Operation(PIDE.editor, view, "find_consts", consume_status(process_indicator, _), (snapshot, results, body) => pretty_text_area.update(snapshot, results, Pretty.separate(body))) private def apply_query(): Unit = { query.addCurrentToHistory() query_operation.apply_query(List(query.getText)) } private val query_label = new Label("Find:") { tooltip = GUI.tooltip_lines("Name / type patterns for constants") } val query = make_query("isabelle-find-consts", query_label.tooltip, apply_query _) /* GUI page */ private val apply_button = new Button("Apply") { tooltip = "Find constants by name / type patterns" reactions += { case ButtonClicked(_) => apply_query() } } private val control_panel = Wrap_Panel( List( query_label, Component.wrap(query), process_indicator.component, apply_button, pretty_text_area.search_label, pretty_text_area.search_field)) def select: Unit = { control_panel.contents += zoom } val page = new TabbedPane.Page("Find Constants", new BorderPanel { layout(control_panel) = BorderPanel.Position.North layout(Component.wrap(pretty_text_area)) = BorderPanel.Position.Center }, apply_button.tooltip) } /* print operation */ private val print_operation = new Query_Dockable.Operation(view) { /* items */ private class Item(val name: String, description: String, sel: Boolean) { val checkbox = new CheckBox(name) { tooltip = "Print " + description selected = sel reactions += { case ButtonClicked(_) => apply_query() } } } private var _items: List[Item] = Nil private def selected_items(): List[String] = for (item <- _items if item.checkbox.selected) yield item.name private def update_items(): List[Item] = { val old_items = _items def was_selected(name: String): Boolean = old_items.find(item => item.name == name) match { case None => false case Some(item) => item.checkbox.selected } _items = for ((name, description) <- Print_Operation.get(PIDE.session)) yield new Item(name, description, was_selected(name)) _items } /* query */ private val process_indicator = new Process_Indicator val query_operation = new Query_Operation(PIDE.editor, view, "print_operation", consume_status(process_indicator, _), (snapshot, results, body) => pretty_text_area.update(snapshot, results, Pretty.separate(body))) private def apply_query(): Unit = query_operation.apply_query(selected_items()) private val query_label = new Label("Print:") def query: JComponent = apply_button.peer update_items() /* GUI page */ private val apply_button = new Button("Apply") { tooltip = "Apply to current context" listenTo(keys) reactions += { case ButtonClicked(_) => apply_query() case evt @ KeyPressed(_, Key.Enter, 0, _) => evt.peer.consume apply_query() } } private val control_panel = Wrap_Panel() def select: Unit = { control_panel.contents.clear() control_panel.contents += query_label update_items().foreach(item => control_panel.contents += item.checkbox) control_panel.contents ++= List(process_indicator.component, apply_button, pretty_text_area.search_label, pretty_text_area.search_field, zoom) } val page = new TabbedPane.Page("Print Context", new BorderPanel { layout(control_panel) = BorderPanel.Position.North layout(Component.wrap(pretty_text_area)) = BorderPanel.Position.Center }, "Print information from context") } /* operations */ private val operations = List(find_theorems, find_consts, print_operation) private val operations_pane = new TabbedPane { pages ++= operations.map(_.page) listenTo(selection) reactions += { case SelectionChanged(_) => select_operation() } } private def get_operation(): Option[Query_Dockable.Operation] = try { Some(operations(operations_pane.selection.index)) } catch { case _: IndexOutOfBoundsException => None } private def select_operation(): Unit = { for (op <- get_operation()) { op.select; op.query.requestFocus() } operations_pane.revalidate() } override def focusOnDefaultComponent(): Unit = { for (op <- get_operation()) op.query.requestFocus() } select_operation() set_content(operations_pane) override def detach_operation: Option[() => Unit] = get_operation() match { case None => None case Some(op) => op.pretty_text_area.detach_operation } /* resize */ private def handle_resize(): Unit = GUI_Thread.require { for (op <- operations) { op.pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } } private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) /* main */ private val main = Session.Consumer[Session.Global_Options](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() } } override def init(): Unit = { PIDE.session.global_options += main handle_resize() operations.foreach(op => op.query_operation.activate()) } override def exit(): Unit = { operations.foreach(op => op.query_operation.deactivate()) PIDE.session.global_options -= main delay_resize.revoke() } } diff --git a/src/Tools/jEdit/src/raw_output_dockable.scala b/src/Tools/jEdit/src/raw_output_dockable.scala --- a/src/Tools/jEdit/src/raw_output_dockable.scala +++ b/src/Tools/jEdit/src/raw_output_dockable.scala @@ -1,37 +1,36 @@ /* Title: Tools/jEdit/src/raw_output_dockable.scala Author: Makarius Dockable window for raw process output (stdout). */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{TextArea, ScrollPane} import org.gjt.sp.jedit.View class Raw_Output_Dockable(view: View, position: String) extends Dockable(view, position) { private val text_area = new TextArea set_content(new ScrollPane(text_area)) /* main */ private val main = Session.Consumer[Prover.Output](getClass.getName) { case output: Prover.Output => GUI_Thread.later { text_area.append(XML.content(output.message)) if (!output.is_stdout && !output.is_stderr) text_area.append("\n") } } override def init(): Unit = { PIDE.session.raw_output_messages += main } override def exit(): Unit = { PIDE.session.raw_output_messages -= main } } diff --git a/src/Tools/jEdit/src/simplifier_trace_dockable.scala b/src/Tools/jEdit/src/simplifier_trace_dockable.scala --- a/src/Tools/jEdit/src/simplifier_trace_dockable.scala +++ b/src/Tools/jEdit/src/simplifier_trace_dockable.scala @@ -1,189 +1,188 @@ /* Title: Tools/jEdit/src/simplifier_trace_dockable.scala Author: Lars Hupel Dockable window with interactive simplifier trace. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Button, CheckBox, Orientation, Separator} import scala.swing.event.ButtonClicked import java.awt.BorderLayout import java.awt.event.{ComponentEvent, ComponentAdapter} import org.gjt.sp.jedit.View class Simplifier_Trace_Dockable(view: View, position: String) extends Dockable(view, position) { GUI_Thread.require {} /* component state -- owned by GUI thread */ private var current_snapshot = Document.State.init.snapshot() private var current_command = Command.empty private var current_results = Command.Results.empty private var current_id = 0L private var do_update = true private val text_area = new Pretty_Text_Area(view) set_content(text_area) private def update_contents(): Unit = { val snapshot = current_snapshot val context = Simplifier_Trace.handle_results(PIDE.session, current_id, current_results) answers.contents.clear() context.questions.values.toList match { case q :: _ => val data = q.data val content = Pretty.separate(XML.Text(data.text) :: data.content) text_area.update(snapshot, Command.Results.empty, content) q.answers.foreach { answer => answers.contents += new Button(answer.string) { reactions += { case ButtonClicked(_) => Simplifier_Trace.send_reply(PIDE.session, data.serial, answer) } } } case Nil => text_area.update(snapshot, Command.Results.empty, Nil) } do_paint() } private def show_trace(): Unit = { val trace = Simplifier_Trace.generate_trace(PIDE.session, current_results) new Simplifier_Trace_Window(view, current_snapshot, trace) } private def do_paint(): Unit = { GUI_Thread.later { text_area.resize(Font_Info.main(PIDE.options.real("jedit_font_scale"))) } } private def handle_resize(): Unit = do_paint() private def handle_update(follow: Boolean): Unit = { val (new_snapshot, new_command, new_results, new_id) = PIDE.editor.current_node_snapshot(view) match { case Some(snapshot) => if (follow && !snapshot.is_outdated) { PIDE.editor.current_command(view, snapshot) match { case Some(cmd) => (snapshot, cmd, snapshot.command_results(cmd), cmd.id) case None => (Document.State.init.snapshot(), Command.empty, Command.Results.empty, 0L) } } else (current_snapshot, current_command, current_results, current_id) case None => (current_snapshot, current_command, current_results, current_id) } current_snapshot = new_snapshot current_command = new_command current_results = new_results current_id = new_id update_contents() } /* main */ private val main = Session.Consumer[Any](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() } case changed: Session.Commands_Changed => GUI_Thread.later { handle_update(do_update) } case Session.Caret_Focus => GUI_Thread.later { handle_update(do_update) } case Simplifier_Trace.Event => GUI_Thread.later { handle_update(do_update) } } override def init(): Unit = { PIDE.session.global_options += main PIDE.session.commands_changed += main PIDE.session.caret_focus += main PIDE.session.trace_events += main handle_update(true) } override def exit(): Unit = { PIDE.session.global_options -= main PIDE.session.commands_changed -= main PIDE.session.caret_focus -= main PIDE.session.trace_events -= main delay_resize.revoke() } /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) /* controls */ private val controls = Wrap_Panel( List( new CheckBox("Auto update") { selected = do_update reactions += { case ButtonClicked(_) => do_update = this.selected handle_update(do_update) } }, new Button("Update") { reactions += { case ButtonClicked(_) => handle_update(true) } }, new Separator(Orientation.Vertical), new Button("Show trace") { reactions += { case ButtonClicked(_) => show_trace() } }, new Button("Clear memory") { reactions += { case ButtonClicked(_) => Simplifier_Trace.clear_memory(PIDE.session) } })) private val answers = Wrap_Panel(Nil, Wrap_Panel.Alignment.Left) add(controls.peer, BorderLayout.NORTH) add(answers.peer, BorderLayout.SOUTH) } diff --git a/src/Tools/jEdit/src/sledgehammer_dockable.scala b/src/Tools/jEdit/src/sledgehammer_dockable.scala --- a/src/Tools/jEdit/src/sledgehammer_dockable.scala +++ b/src/Tools/jEdit/src/sledgehammer_dockable.scala @@ -1,172 +1,171 @@ /* Title: Tools/jEdit/src/sledgehammer_dockable.scala Author: Makarius Dockable window for Sledgehammer. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Button, Component, Label, CheckBox} import scala.swing.event.ButtonClicked import java.awt.BorderLayout import java.awt.event.{ComponentEvent, ComponentAdapter, KeyEvent} import org.gjt.sp.jedit.View import org.gjt.sp.jedit.gui.HistoryTextField class Sledgehammer_Dockable(view: View, position: String) extends Dockable(view, position) { GUI_Thread.require {} /* text area */ val pretty_text_area = new Pretty_Text_Area(view) set_content(pretty_text_area) override def detach_operation: Option[() => Unit] = pretty_text_area.detach_operation /* query operation */ private val process_indicator = new Process_Indicator private def consume_status(status: Query_Operation.Status.Value): Unit = { status match { case Query_Operation.Status.WAITING => process_indicator.update("Waiting for evaluation of context ...", 5) case Query_Operation.Status.RUNNING => process_indicator.update("Sledgehammering ...", 15) case Query_Operation.Status.FINISHED => process_indicator.update(null, 0) } } private val sledgehammer = new Query_Operation(PIDE.editor, view, "sledgehammer", consume_status, (snapshot, results, body) => pretty_text_area.update(snapshot, results, Pretty.separate(body))) /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) private def handle_resize(): Unit = { GUI_Thread.require {} pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } /* controls */ private def clicked: Unit = { provers.addCurrentToHistory() PIDE.options.string("sledgehammer_provers") = provers.getText sledgehammer.apply_query( List(provers.getText, isar_proofs.selected.toString, try0.selected.toString)) } private val provers_label = new Label("Provers:") { tooltip = GUI.tooltip_lines( "Automatic provers as space-separated list, e.g.\n" + PIDE.options.value.check_name("sledgehammer_provers").default_value) } private val provers = new HistoryTextField("isabelle-sledgehammer-provers") { override def processKeyEvent(evt: KeyEvent): Unit = { if (evt.getID == KeyEvent.KEY_PRESSED && evt.getKeyCode == KeyEvent.VK_ENTER) clicked super.processKeyEvent(evt) } setToolTipText(provers_label.tooltip) setColumns(30) } private def update_provers(): Unit = { val new_provers = PIDE.options.string("sledgehammer_provers") if (new_provers != provers.getText) { provers.setText(new_provers) if (provers.getCaret != null) provers.getCaret.setDot(0) } } private val isar_proofs = new CheckBox("Isar proofs") { tooltip = "Specify whether Isar proofs should be output in addition to \"by\" one-liner" selected = false } private val try0 = new CheckBox("Try methods") { tooltip = "Try standard proof methods like \"auto\" and \"blast\" as alternatives to \"metis\"" selected = true } private val apply_query = new Button("Apply") { tooltip = "Search for first-order proof using automatic theorem provers" reactions += { case ButtonClicked(_) => clicked } } private val cancel_query = new Button("Cancel") { tooltip = "Interrupt unfinished sledgehammering" reactions += { case ButtonClicked(_) => sledgehammer.cancel_query() } } private val locate_query = new Button("Locate") { tooltip = "Locate context of current query within source text" reactions += { case ButtonClicked(_) => sledgehammer.locate_query() } } private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private val controls = Wrap_Panel( List(provers_label, Component.wrap(provers), isar_proofs, try0, process_indicator.component, apply_query, cancel_query, locate_query, zoom)) add(controls.peer, BorderLayout.NORTH) override def focusOnDefaultComponent(): Unit = provers.requestFocus() /* main */ private val main = Session.Consumer[Session.Global_Options](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { update_provers(); handle_resize() } } override def init(): Unit = { PIDE.session.global_options += main update_provers() handle_resize() sledgehammer.activate() } override def exit(): Unit = { sledgehammer.deactivate() PIDE.session.global_options -= main delay_resize.revoke() } } diff --git a/src/Tools/jEdit/src/state_dockable.scala b/src/Tools/jEdit/src/state_dockable.scala --- a/src/Tools/jEdit/src/state_dockable.scala +++ b/src/Tools/jEdit/src/state_dockable.scala @@ -1,147 +1,146 @@ /* Title: Tools/jEdit/src/state_dockable.scala Author: Makarius Dockable window for proof state output. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Button, CheckBox} import scala.swing.event.ButtonClicked import java.awt.BorderLayout import java.awt.event.{ComponentEvent, ComponentAdapter} import org.gjt.sp.jedit.View class State_Dockable(view: View, position: String) extends Dockable(view, position) { GUI_Thread.require {} /* text area */ val pretty_text_area = new Pretty_Text_Area(view) set_content(pretty_text_area) override def detach_operation: Option[() => Unit] = pretty_text_area.detach_operation private val print_state = new Query_Operation(PIDE.editor, view, "print_state", _ => (), (snapshot, results, body) => pretty_text_area.update(snapshot, results, Pretty.separate(body))) /* resize */ private val delay_resize = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { handle_resize() } addComponentListener(new ComponentAdapter { override def componentResized(e: ComponentEvent): Unit = delay_resize.invoke() override def componentShown(e: ComponentEvent): Unit = delay_resize.invoke() }) private def handle_resize(): Unit = { GUI_Thread.require {} pretty_text_area.resize( Font_Info.main(PIDE.options.real("jedit_font_scale") * zoom.factor / 100)) } /* update */ def update_request(): Unit = GUI_Thread.require { print_state.apply_query(Nil) } def update(): Unit = { GUI_Thread.require {} PIDE.editor.current_node_snapshot(view) match { case Some(snapshot) => (PIDE.editor.current_command(view, snapshot), print_state.get_location) match { case (Some(command1), Some(command2)) if command1.id == command2.id => case _ => update_request() } case None => } } /* auto update */ private var auto_update_enabled = true private def auto_update(): Unit = GUI_Thread.require { if (auto_update_enabled) update() } /* controls */ private val auto_update_button = new CheckBox("Auto update") { tooltip = "Indicate automatic update following cursor movement" reactions += { case ButtonClicked(_) => auto_update_enabled = this.selected; auto_update() } selected = auto_update_enabled } private val update_button = new Button("Update") { tooltip = "Update display according to the command at cursor position" reactions += { case ButtonClicked(_) => update_request() } } private val locate_button = new Button("Locate") { tooltip = "Locate printed command within source text" reactions += { case ButtonClicked(_) => print_state.locate_query() } } private val zoom = new Font_Info.Zoom_Box { def changed = handle_resize() } private val controls = Wrap_Panel( List(auto_update_button, update_button, locate_button, pretty_text_area.search_label, pretty_text_area.search_field, zoom)) add(controls.peer, BorderLayout.NORTH) /* main */ private val main = Session.Consumer[Any](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { handle_resize() } case changed: Session.Commands_Changed => if (changed.assignment) GUI_Thread.later { auto_update() } case Session.Caret_Focus => GUI_Thread.later { auto_update() } } override def init(): Unit = { PIDE.session.global_options += main PIDE.session.commands_changed += main PIDE.session.caret_focus += main handle_resize() print_state.activate() auto_update() } override def exit(): Unit = { print_state.deactivate() PIDE.session.caret_focus -= main PIDE.session.global_options -= main PIDE.session.commands_changed -= main delay_resize.revoke() } } diff --git a/src/Tools/jEdit/src/symbols_dockable.scala b/src/Tools/jEdit/src/symbols_dockable.scala --- a/src/Tools/jEdit/src/symbols_dockable.scala +++ b/src/Tools/jEdit/src/symbols_dockable.scala @@ -1,222 +1,221 @@ /* Title: Tools/jEdit/src/symbols_dockable.scala Author: Fabian Immler Dockable window for Symbol Palette. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.event.{SelectionChanged, ValueChanged} import scala.swing.{Component, Action, Button, TabbedPane, TextField, BorderPanel, Label, ScrollPane} import org.gjt.sp.jedit.{EditBus, EBComponent, EBMessage, View} class Symbols_Dockable(view: View, position: String) extends Dockable(view, position) { private def font_size: Int = Font_Info.main_size(PIDE.options.real("jedit_font_scale")).round /* abbrevs */ private val abbrevs_panel = new Abbrevs_Panel private val abbrevs_refresh_delay = Delay.last(PIDE.options.seconds("editor_update_delay"), gui = true) { abbrevs_panel.refresh } private class Abbrev_Component(txt: String, abbrs: List[String]) extends Button { def update_font: Unit = { font = GUI.font(size = font_size) } update_font text = "" + HTML.output(Symbol.decode(txt)) + "" action = Action(text) { val text_area = view.getTextArea val (s1, s2) = Completion.split_template(Isabelle_Encoding.perhaps_decode(text_area.getBuffer, txt)) text_area.setSelectedText(s1 + s2) text_area.moveCaretPosition(text_area.getCaretPosition - s2.length) text_area.requestFocus() } tooltip = GUI.tooltip_lines(cat_lines(txt :: abbrs.map(a => "abbrev: " + a))) } private class Abbrevs_Panel extends Wrap_Panel(Nil, Wrap_Panel.Alignment.Center) { private var abbrevs: Thy_Header.Abbrevs = Nil def refresh: Unit = GUI_Thread.require { val abbrevs1 = Isabelle.buffer_syntax(view.getBuffer).getOrElse(Outer_Syntax.empty).abbrevs if (abbrevs != abbrevs1) { abbrevs = abbrevs1 val entries: List[(String, List[String])] = Multi_Map( (for { (abbr, txt0) <- abbrevs txt = Symbol.encode(txt0) if !Symbol.iterator(txt). forall(s => s.length == 1 && s(0) != Completion.caret_indicator) } yield (txt, abbr)): _*).iterator_list.toList contents.clear() for ((txt, abbrs) <- entries.sortBy(_._1)) contents += new Abbrev_Component(txt, abbrs.filterNot(_ == "").sorted) revalidate() repaint() } } refresh } /* symbols */ private class Symbol_Component(val symbol: String, is_control: Boolean) extends Button { def update_font: Unit = { font = Symbol.fonts.get(symbol) match { case None => GUI.font(size = font_size) case Some(font_family) => GUI.font(family = font_family, size = font_size) } } update_font action = Action(Symbol.decode(symbol)) { val text_area = view.getTextArea if (is_control && HTML.is_control(symbol)) Syntax_Style.edit_control_style(text_area, symbol) else text_area.setSelectedText(Isabelle_Encoding.perhaps_decode(text_area.getBuffer, symbol)) text_area.requestFocus() } tooltip = GUI.tooltip_lines( cat_lines(symbol :: Symbol.abbrevs.get_list(symbol).map(a => "abbrev: " + a))) } private class Reset_Component extends Button { action = Action("Reset") { val text_area = view.getTextArea Syntax_Style.edit_control_style(text_area, "") text_area.requestFocus() } tooltip = "Reset control symbols within text" } /* search */ private class Search_Panel extends BorderPanel { val search_field = new TextField(10) val results_panel = Wrap_Panel(Nil, Wrap_Panel.Alignment.Center) layout(search_field) = BorderPanel.Position.North layout(new ScrollPane(results_panel)) = BorderPanel.Position.Center val search_space = for ((sym, _) <- Symbol.codes) yield (sym, Word.lowercase(sym)) val search_delay = Delay.last(PIDE.options.seconds("editor_input_delay"), gui = true) { val search_words = Word.explode(Word.lowercase(search_field.text)) val search_limit = PIDE.options.int("jedit_symbols_search_limit") max 0 val results = for ((sym, s) <- search_space; if search_words.forall(s.contains(_))) yield sym results_panel.contents.clear() for (sym <- results.take(search_limit)) results_panel.contents += new Symbol_Component(sym, false) if (results.length > search_limit) results_panel.contents += new Label("...") { tooltip = "(" + (results.length - search_limit) + " more)" } revalidate() repaint() } search_field.reactions += { case ValueChanged(_) => search_delay.invoke() } } /* tabs */ private val group_tabs: TabbedPane = new TabbedPane { pages += new TabbedPane.Page("abbrevs", abbrevs_panel) pages ++= Symbol.groups_code.map({ case (group, symbols) => val control = group == "control" new TabbedPane.Page(group, new ScrollPane(Wrap_Panel( symbols.map(new Symbol_Component(_, control)) ::: (if (control) List(new Reset_Component) else Nil), Wrap_Panel.Alignment.Center)), null) }) val search_panel = new Search_Panel val search_page = new TabbedPane.Page("search", search_panel, "Search Symbols") pages += search_page listenTo(selection) reactions += { case SelectionChanged(_) if selection.page == search_page => search_panel.search_field.requestFocus() } for (page <- pages) page.title = Word.implode(Word.explode('_', page.title).map(Word.perhaps_capitalize)) } set_content(group_tabs) /* main */ private val edit_bus_handler: EBComponent = new EBComponent { def handleMessage(msg: EBMessage): Unit = abbrevs_refresh_delay.invoke() } private val main = Session.Consumer[Any](getClass.getName) { case _: Session.Global_Options => GUI_Thread.later { val comp = group_tabs.peer GUI.traverse_components(comp, { case c0: javax.swing.JComponent => Component.wrap(c0) match { case c: Abbrev_Component => c.update_font case c: Symbol_Component => c.update_font case _ => } case _ => }) comp.revalidate() comp.repaint() } case _: Session.Commands_Changed => abbrevs_refresh_delay.invoke() } override def init(): Unit = { EditBus.addToBus(edit_bus_handler) PIDE.session.global_options += main PIDE.session.commands_changed += main } override def exit(): Unit = { EditBus.removeFromBus(edit_bus_handler) PIDE.session.global_options -= main PIDE.session.commands_changed -= main } } diff --git a/src/Tools/jEdit/src/syntax_style.scala b/src/Tools/jEdit/src/syntax_style.scala --- a/src/Tools/jEdit/src/syntax_style.scala +++ b/src/Tools/jEdit/src/syntax_style.scala @@ -1,189 +1,209 @@ /* Title: Tools/jEdit/src/syntax_style.scala Author: Makarius Support for extended syntax styles: subscript, superscript, bold, user fonts. */ package isabelle.jedit import isabelle._ import java.util.{Map => JMap} import java.awt.{Font, Color} import java.awt.font.TextAttribute import java.awt.geom.AffineTransform import org.gjt.sp.util.SyntaxUtilities +import org.gjt.sp.jedit.jEdit import org.gjt.sp.jedit.syntax.{Token => JEditToken, SyntaxStyle} import org.gjt.sp.jedit.textarea.TextArea object Syntax_Style { /* extended syntax styles */ private val plain_range: Int = JEditToken.ID_COUNT + private val full_range = 6 * plain_range + 1 private def check_range(i: Int): Unit = require(0 <= i && i < plain_range, "bad syntax style range") def subscript(i: Byte): Byte = { check_range(i); (i + plain_range).toByte } def superscript(i: Byte): Byte = { check_range(i); (i + 2 * plain_range).toByte } def bold(i: Byte): Byte = { check_range(i); (i + 3 * plain_range).toByte } def user_font(idx: Int, i: Byte): Byte = { check_range(i); (i + (4 + idx) * plain_range).toByte } val hidden: Byte = (6 * plain_range).toByte val control: Byte = (hidden + JEditToken.DIGIT).toByte private def font_style(style: SyntaxStyle, f: Font => Font): SyntaxStyle = new SyntaxStyle(style.getForegroundColor, style.getBackgroundColor, f(style.getFont)) private def script_style(style: SyntaxStyle, i: Int): SyntaxStyle = { font_style(style, font0 => { val font1 = font0.deriveFont(JMap.of(TextAttribute.SUPERSCRIPT, java.lang.Integer.valueOf(i))) def shift(y: Float): Font = GUI.transform_font(font1, AffineTransform.getTranslateInstance(0.0, y.toDouble)) val m0 = GUI.line_metrics(font0) val m1 = GUI.line_metrics(font1) val a = m1.getAscent - m0.getAscent val b = (m1.getDescent + m1.getLeading) - (m0.getDescent + m0.getLeading) if (a > 0.0f) shift(a) else if (b > 0.0f) shift(- b) else font1 }) } private def bold_style(style: SyntaxStyle): SyntaxStyle = font_style(style, font => font.deriveFont(if (font.isBold) Font.PLAIN else Font.BOLD)) val hidden_color: Color = new Color(255, 255, 255, 0) + def set_extender(extender: SyntaxUtilities.StyleExtender): Unit = + { + SyntaxUtilities.setStyleExtender(extender) + GUI_Thread.later { jEdit.propertiesChanged } + } + + object Base_Extender extends SyntaxUtilities.StyleExtender + { + override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = + { + val new_styles = new Array[SyntaxStyle](full_range) + for (i <- 0 until full_range) { + new_styles(i) = styles(i % plain_range) + } + new_styles + } + } + object Extender extends SyntaxUtilities.StyleExtender { val max_user_fonts = 2 if (Symbol.font_names.length > max_user_fonts) error("Too many user symbol fonts (" + max_user_fonts + " permitted): " + Symbol.font_names.mkString(", ")) override def extendStyles(styles: Array[SyntaxStyle]): Array[SyntaxStyle] = { val style0 = styles(0) val font0 = style0.getFont val new_styles = Array.fill[SyntaxStyle](java.lang.Byte.MAX_VALUE)(styles(0)) for (i <- 0 until plain_range) { val style = styles(i) new_styles(i) = style new_styles(subscript(i.toByte)) = script_style(style, -1) new_styles(superscript(i.toByte)) = script_style(style, 1) new_styles(bold(i.toByte)) = bold_style(style) for (idx <- 0 until max_user_fonts) new_styles(user_font(idx, i.toByte)) = style for ((family, idx) <- Symbol.font_index) new_styles(user_font(idx, i.toByte)) = font_style(style, GUI.imitate_font(_, family)) } new_styles(hidden) = new SyntaxStyle(hidden_color, null, GUI.transform_font(new Font(font0.getFamily, 0, 1), AffineTransform.getScaleInstance(2.0, font0.getSize.toDouble))) new_styles(control) = new SyntaxStyle(style0.getForegroundColor, style0.getBackgroundColor, { val font_style = (if (font0.isItalic) 0 else Font.ITALIC) | (if (font0.isBold) 0 else Font.BOLD) new Font(font0.getFamily, font_style, font0.getSize) }) new_styles } } private def control_style(sym: String): Option[Byte => Byte] = if (sym == Symbol.sub_decoded) Some(subscript) else if (sym == Symbol.sup_decoded) Some(superscript) else if (sym == Symbol.bold_decoded) Some(bold) else None def extended(text: CharSequence): Map[Text.Offset, Byte => Byte] = { var result = Map[Text.Offset, Byte => Byte]() def mark(start: Text.Offset, stop: Text.Offset, style: Byte => Byte): Unit = { for (i <- start until stop) result += (i -> style) } var offset = 0 var control_sym = "" for (sym <- Symbol.iterator(text)) { val end_offset = offset + sym.length if (control_style(sym).isDefined) control_sym = sym else if (control_sym != "") { if (Symbol.is_controllable(sym) && !Symbol.fonts.isDefinedAt(sym)) { mark(offset - control_sym.length, offset, _ => hidden) mark(offset, end_offset, control_style(control_sym).get) } control_sym = "" } if (Symbol.is_control_encoded(sym)) { val a = offset + Symbol.control_prefix.length val b = end_offset - Symbol.control_suffix.length mark(offset, a, _ => hidden) mark(a, b, _ => control) mark(b, end_offset, _ => hidden) } Symbol.lookup_font(sym) match { case Some(idx) => mark(offset, end_offset, user_font(idx, _)) case _ => } offset = end_offset } result } /* editing support for control symbols */ def edit_control_style(text_area: TextArea, control_sym: String): Unit = { GUI_Thread.assert {} val buffer = text_area.getBuffer val control_decoded = Isabelle_Encoding.perhaps_decode(buffer, control_sym) def update_style(text: String): String = { val result = new StringBuilder for (sym <- Symbol.iterator(text) if !HTML.is_control(sym)) { if (Symbol.is_controllable(sym)) result ++= control_decoded result ++= sym } result.toString } text_area.getSelection.foreach(sel => { val before = JEdit_Lib.point_range(buffer, sel.getStart - 1) JEdit_Lib.get_text(buffer, before) match { case Some(s) if HTML.is_control(s) => text_area.extendSelection(before.start, before.stop) case _ => } }) text_area.getSelection.toList match { case Nil => text_area.setSelectedText(control_decoded) case sels => JEdit_Lib.buffer_edit(buffer) { sels.foreach(sel => text_area.setSelectedText(sel, update_style(text_area.getSelectedText(sel)))) } } } } diff --git a/src/Tools/jEdit/src/syslog_dockable.scala b/src/Tools/jEdit/src/syslog_dockable.scala --- a/src/Tools/jEdit/src/syslog_dockable.scala +++ b/src/Tools/jEdit/src/syslog_dockable.scala @@ -1,48 +1,47 @@ /* Title: Tools/jEdit/src/syslog_dockable.scala Author: Makarius Dockable window for syslog. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{TextArea, ScrollPane} import org.gjt.sp.jedit.View class Syslog_Dockable(view: View, position: String) extends Dockable(view, position) { /* GUI components */ private val syslog = new TextArea() private def syslog_delay = Delay.first(PIDE.options.seconds("editor_update_delay"), gui = true) { val text = PIDE.session.syslog_content() if (text != syslog.text) syslog.text = text } set_content(new ScrollPane(syslog)) /* main */ private val main = Session.Consumer[Prover.Output](getClass.getName) { case _ => syslog_delay.invoke() } override def init(): Unit = { PIDE.session.syslog_messages += main syslog_delay.invoke() } override def exit(): Unit = { PIDE.session.syslog_messages -= main } } diff --git a/src/Tools/jEdit/src/theories_dockable.scala b/src/Tools/jEdit/src/theories_dockable.scala --- a/src/Tools/jEdit/src/theories_dockable.scala +++ b/src/Tools/jEdit/src/theories_dockable.scala @@ -1,275 +1,274 @@ /* Title: Tools/jEdit/src/theories_dockable.scala Author: Makarius Dockable window for theories managed by prover. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Button, TextArea, Label, ListView, Alignment, ScrollPane, Component, CheckBox, BorderPanel} import scala.swing.event.{ButtonClicked, MouseClicked, MouseMoved} import java.awt.{BorderLayout, Graphics2D, Color, Point, Dimension} import javax.swing.{JList, BorderFactory, UIManager} import javax.swing.border.{BevelBorder, SoftBevelBorder} import org.gjt.sp.jedit.{View, jEdit} class Theories_Dockable(view: View, position: String) extends Dockable(view, position) { /* status */ private val status = new ListView(List.empty[Document.Node.Name]) { background = { // enforce default value val c = UIManager.getDefaults.getColor("Panel.background") new Color(c.getRed, c.getGreen, c.getBlue, c.getAlpha) } listenTo(mouse.clicks) listenTo(mouse.moves) reactions += { case MouseClicked(_, point, _, clicks, _) => val index = peer.locationToIndex(point) if (index >= 0) { if (in_checkbox(peer.indexToLocation(index), point)) { if (clicks == 1) Document_Model.node_required(listData(index), toggle = true) } else if (clicks == 2) PIDE.editor.goto_file(true, view, listData(index).node) } case MouseMoved(_, point, _) => val index = peer.locationToIndex(point) val index_location = peer.indexToLocation(index) if (index >= 0 && in_checkbox(index_location, point)) tooltip = "Mark as required for continuous checking" else if (index >= 0 && in_label(index_location, point)) { val name = listData(index) val st = nodes_status.overall_node_status(name) tooltip = "theory " + quote(name.theory) + (if (st == Document_Status.Overall_Node_Status.ok) "" else " (" + st + ")") } else tooltip = null } } status.peer.setLayoutOrientation(JList.HORIZONTAL_WRAP) status.peer.setVisibleRowCount(0) status.selection.intervalMode = ListView.IntervalMode.Single set_content(new ScrollPane(status)) /* controls */ def phase_text(phase: Session.Phase): String = "Prover: " + phase.print private val session_phase = new Label(phase_text(PIDE.session.phase)) session_phase.border = new SoftBevelBorder(BevelBorder.LOWERED) session_phase.tooltip = "Status of prover session" private def handle_phase(phase: Session.Phase): Unit = { GUI_Thread.require {} session_phase.text = " " + phase_text(phase) + " " } private val purge = new Button("Purge") { tooltip = "Restrict document model to theories required for open editor buffers" reactions += { case ButtonClicked(_) => PIDE.editor.purge() } } private val continuous_checking = new Isabelle.Continuous_Checking continuous_checking.focusable = false private val logic = JEdit_Sessions.logic_selector(PIDE.options, true) private val controls = Wrap_Panel(List(purge, continuous_checking, session_phase, logic)) add(controls.peer, BorderLayout.NORTH) /* component state -- owned by GUI thread */ private var nodes_status = Document_Status.Nodes_Status.empty private var nodes_required: Set[Document.Node.Name] = Document_Model.required_nodes() private def in(geometry: Option[(Point, Dimension)], loc0: Point, p: Point): Boolean = geometry match { case Some((loc, size)) => loc0.x + loc.x <= p.x && p.x < loc0.x + size.width && loc0.y + loc.y <= p.y && p.y < loc0.y + size.height case None => false } private def in_checkbox(loc0: Point, p: Point): Boolean = Node_Renderer_Component != null && in(Node_Renderer_Component.checkbox_geometry, loc0, p) private def in_label(loc0: Point, p: Point): Boolean = Node_Renderer_Component != null && in(Node_Renderer_Component.label_geometry, loc0, p) private object Node_Renderer_Component extends BorderPanel { opaque = true border = BorderFactory.createEmptyBorder(2, 2, 2, 2) var node_name = Document.Node.Name.empty var checkbox_geometry: Option[(Point, Dimension)] = None val checkbox = new CheckBox { opaque = false override def paintComponent(gfx: Graphics2D): Unit = { super.paintComponent(gfx) if (location != null && size != null) checkbox_geometry = Some((location, size)) } } var label_geometry: Option[(Point, Dimension)] = None val label = new Label { background = view.getTextArea.getPainter.getBackground foreground = view.getTextArea.getPainter.getForeground opaque = false xAlignment = Alignment.Leading override def paintComponent(gfx: Graphics2D): Unit = { def paint_segment(x: Int, w: Int, color: Color): Unit = { gfx.setColor(color) gfx.fillRect(x, 0, w, size.height) } paint_segment(0, size.width, background) nodes_status.get(node_name) match { case Some(node_status) => val segments = List( (node_status.unprocessed, PIDE.options.color_value("unprocessed1_color")), (node_status.running, PIDE.options.color_value("running_color")), (node_status.warned, PIDE.options.color_value("warning_color")), (node_status.failed, PIDE.options.color_value("error_color")) ).filter(_._1 > 0) segments.foldLeft(size.width - 2) { case (last, (n, color)) => val w = (n * ((size.width - 4) - segments.length) / node_status.total) max 4 paint_segment(last - w, w, color) last - w - 1 } case None => paint_segment(0, size.width, PIDE.options.color_value("unprocessed1_color")) } super.paintComponent(gfx) if (location != null && size != null) label_geometry = Some((location, size)) } } def label_border(name: Document.Node.Name): Unit = { val st = nodes_status.overall_node_status(name) val color = st match { case Document_Status.Overall_Node_Status.ok => PIDE.options.color_value("ok_color") case Document_Status.Overall_Node_Status.failed => PIDE.options.color_value("failed_color") case _ => label.foreground } val thickness1 = if (st == Document_Status.Overall_Node_Status.pending) 1 else 3 val thickness2 = 4 - thickness1 label.border = BorderFactory.createCompoundBorder( BorderFactory.createLineBorder(color, thickness1), BorderFactory.createEmptyBorder(thickness2, thickness2, thickness2, thickness2)) } layout(checkbox) = BorderPanel.Position.West layout(label) = BorderPanel.Position.Center } private class Node_Renderer extends ListView.Renderer[Document.Node.Name] { def componentFor(list: ListView[_ <: isabelle.Document.Node.Name], isSelected: Boolean, focused: Boolean, name: Document.Node.Name, index: Int): Component = { val component = Node_Renderer_Component component.node_name = name component.checkbox.selected = nodes_required.contains(name) component.label_border(name) component.label.text = name.theory_base_name component } } status.renderer = new Node_Renderer private def handle_update( domain: Option[Set[Document.Node.Name]] = None, trim: Boolean = false): Unit = { GUI_Thread.require {} val snapshot = PIDE.session.snapshot() val (nodes_status_changed, nodes_status1) = nodes_status.update( PIDE.resources, snapshot.state, snapshot.version, domain = domain, trim = trim) nodes_status = nodes_status1 if (nodes_status_changed) { status.listData = (for { (name, node_status) <- nodes_status1.present.iterator if !node_status.is_suppressed && node_status.total > 0 } yield name).toList } } /* main */ private val main = Session.Consumer[Any](getClass.getName) { case phase: Session.Phase => GUI_Thread.later { handle_phase(phase) } case _: Session.Global_Options => GUI_Thread.later { continuous_checking.load() logic.load () nodes_required = Document_Model.required_nodes() status.repaint() } case changed: Session.Commands_Changed => GUI_Thread.later { handle_update(domain = Some(changed.nodes), trim = changed.assignment) } } override def init(): Unit = { PIDE.session.phase_changed += main PIDE.session.global_options += main PIDE.session.commands_changed += main handle_phase(PIDE.session.phase) handle_update() } override def exit(): Unit = { PIDE.session.phase_changed -= main PIDE.session.global_options -= main PIDE.session.commands_changed -= main } } diff --git a/src/Tools/jEdit/src/timing_dockable.scala b/src/Tools/jEdit/src/timing_dockable.scala --- a/src/Tools/jEdit/src/timing_dockable.scala +++ b/src/Tools/jEdit/src/timing_dockable.scala @@ -1,224 +1,223 @@ /* Title: Tools/jEdit/src/timing_dockable.scala Author: Makarius Dockable window for timing information. */ package isabelle.jedit import isabelle._ -import isabelle.jedit_base.Dockable import scala.swing.{Label, ListView, Alignment, ScrollPane, Component, TextField} import scala.swing.event.{MouseClicked, ValueChanged} import java.awt.{BorderLayout, Graphics2D, Insets, Color} import javax.swing.{JList, BorderFactory} import javax.swing.border.{BevelBorder, SoftBevelBorder} import org.gjt.sp.jedit.{View, jEdit} class Timing_Dockable(view: View, position: String) extends Dockable(view, position) { /* entry */ private object Entry { object Ordering extends scala.math.Ordering[Entry] { def compare(entry1: Entry, entry2: Entry): Int = entry2.timing compare entry1.timing } object Renderer_Component extends Label { opaque = false xAlignment = Alignment.Leading border = BorderFactory.createEmptyBorder(2, 2, 2, 2) var entry: Entry = null override def paintComponent(gfx: Graphics2D): Unit = { def paint_rectangle(color: Color): Unit = { val size = peer.getSize() val insets = border.getBorderInsets(peer) val x = insets.left val y = insets.top val w = size.width - x - insets.right val h = size.height - y - insets.bottom gfx.setColor(color) gfx.fillRect(x, y, w, h) } entry match { case theory_entry: Theory_Entry if theory_entry.current => paint_rectangle(view.getTextArea.getPainter.getSelectionColor) case _: Command_Entry => paint_rectangle(view.getTextArea.getPainter.getMultipleSelectionColor) case _ => } super.paintComponent(gfx) } } class Renderer extends ListView.Renderer[Entry] { def componentFor(list: ListView[_ <: Timing_Dockable.this.Entry], isSelected: Boolean, focused: Boolean, entry: Entry, index: Int): Component = { val component = Renderer_Component component.entry = entry component.text = entry.print component } } } private abstract class Entry { def timing: Double def print: String def follow(snapshot: Document.Snapshot): Unit } private case class Theory_Entry(name: Document.Node.Name, timing: Double, current: Boolean) extends Entry { def print: String = Time.print_seconds(timing) + "s theory " + quote(name.theory) def follow(snapshot: Document.Snapshot): Unit = PIDE.editor.goto_file(true, view, name.node) } private case class Command_Entry(command: Command, timing: Double) extends Entry { def print: String = " " + Time.print_seconds(timing) + "s command " + quote(command.span.name) def follow(snapshot: Document.Snapshot): Unit = PIDE.editor.hyperlink_command(true, snapshot, command.id).foreach(_.follow(view)) } /* timing view */ private val timing_view = new ListView(List.empty[Entry]) { listenTo(mouse.clicks) reactions += { case MouseClicked(_, point, _, clicks, _) if clicks == 2 => val index = peer.locationToIndex(point) if (index >= 0) listData(index).follow(PIDE.session.snapshot()) } } timing_view.peer.setLayoutOrientation(JList.VERTICAL_WRAP) timing_view.peer.setVisibleRowCount(0) timing_view.selection.intervalMode = ListView.IntervalMode.Single timing_view.renderer = new Entry.Renderer set_content(new ScrollPane(timing_view)) /* timing threshold */ private var timing_threshold = PIDE.options.real("jedit_timing_threshold") private val threshold_tooltip = "Threshold for timing display (seconds)" private val threshold_label = new Label("Threshold: ") { tooltip = threshold_tooltip } private val threshold_value = new TextField(Time.print_seconds(timing_threshold)) { reactions += { case _: ValueChanged => text match { case Value.Double(x) if x >= 0.0 => timing_threshold = x case _ => } handle_update() } tooltip = threshold_tooltip verifier = ((s: String) => s match { case Value.Double(x) => x >= 0.0 case _ => false }) } private val controls = Wrap_Panel(List(threshold_label, threshold_value)) add(controls.peer, BorderLayout.NORTH) /* component state -- owned by GUI thread */ private var nodes_timing = Map.empty[Document.Node.Name, Document_Status.Overall_Timing] private def make_entries(): List[Entry] = { GUI_Thread.require {} val name = Document_View.get(view.getTextArea) match { case None => Document.Node.Name.empty case Some(doc_view) => doc_view.model.node_name } val timing = nodes_timing.getOrElse(name, Document_Status.Overall_Timing.empty) val theories = (for ((node_name, node_timing) <- nodes_timing.toList if node_timing.command_timings.nonEmpty) yield Theory_Entry(node_name, node_timing.total, false)).sorted(Entry.Ordering) val commands = (for ((command, command_timing) <- timing.command_timings.toList) yield Command_Entry(command, command_timing)).sorted(Entry.Ordering) theories.flatMap(entry => if (entry.name == name) entry.copy(current = true) :: commands else List(entry)) } private def handle_update(restriction: Option[Set[Document.Node.Name]] = None): Unit = { GUI_Thread.require {} val snapshot = PIDE.session.snapshot() val nodes_timing1 = (restriction match { case Some(names) => names.iterator.map(name => (name, snapshot.get_node(name))) case None => snapshot.version.nodes.iterator }).foldLeft(nodes_timing) { case (timing1, (name, node)) => if (PIDE.resources.session_base.loaded_theory(name)) timing1 else { val node_timing = Document_Status.Overall_Timing.make( snapshot.state, snapshot.version, node.commands, threshold = timing_threshold) timing1 + (name -> node_timing) } } nodes_timing = nodes_timing1 val entries = make_entries() if (timing_view.listData.toList != entries) timing_view.listData = entries } /* main */ private val main = Session.Consumer[Session.Commands_Changed](getClass.getName) { case changed => GUI_Thread.later { handle_update(Some(changed.nodes)) } } override def init(): Unit = { PIDE.session.commands_changed += main handle_update() } override def exit(): Unit = { PIDE.session.commands_changed -= main } }