From a15a0540aefaa72644f51f80d6c9aa58f74efcac Mon Sep 17 00:00:00 2001 From: Simeon Andreev Date: Thu, 21 Mar 2024 13:23:45 +0200 Subject: [PATCH 01/19] Updated bundles with comparator errors See: eclipse-platform/eclipse.platform.releng.aggregator#1923 --- org.eclipse.jdt.bcoview.feature/feature.xml | 2 +- org.eclipse.jdt.bcoview/META-INF/MANIFEST.MF | 2 +- org.eclipse.jdt.bcoview/pom.xml | 2 +- org.eclipse.jdt.jeview.feature/feature.xml | 2 +- org.eclipse.jdt.jeview/META-INF/MANIFEST.MF | 2 +- org.eclipse.jdt.junit.core/forceQualifierUpdate.txt | 3 ++- org.eclipse.jdt.junit/forceQualifierUpdate.txt | 3 ++- org.eclipse.jdt.text.tests/forceQualifierUpdate.txt | 3 ++- org.eclipse.jdt.ui.tests.refactoring/META-INF/MANIFEST.MF | 2 +- org.eclipse.jdt.ui.tests.refactoring/pom.xml | 2 +- org.eclipse.jdt.ui.unittest.junit/forceQualifierUpdate.txt | 3 ++- org.eclipse.jdt.ui/forceQualifierUpdate.txt | 3 ++- 12 files changed, 17 insertions(+), 12 deletions(-) diff --git a/org.eclipse.jdt.bcoview.feature/feature.xml b/org.eclipse.jdt.bcoview.feature/feature.xml index 0a9216125a6..1cd16254334 100644 --- a/org.eclipse.jdt.bcoview.feature/feature.xml +++ b/org.eclipse.jdt.bcoview.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jdt.bcoview/META-INF/MANIFEST.MF b/org.eclipse.jdt.bcoview/META-INF/MANIFEST.MF index df6131a3b03..67f1adfe3c0 100644 --- a/org.eclipse.jdt.bcoview/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.bcoview/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.bcoview;singleton:=true -Bundle-Version: 1.2.300.qualifier +Bundle-Version: 1.2.400.qualifier Bundle-ClassPath: . Bundle-Activator: org.eclipse.jdt.bcoview.BytecodeOutlinePlugin Bundle-Vendor: %providerName diff --git a/org.eclipse.jdt.bcoview/pom.xml b/org.eclipse.jdt.bcoview/pom.xml index afc033a0bfb..a0cd1769619 100644 --- a/org.eclipse.jdt.bcoview/pom.xml +++ b/org.eclipse.jdt.bcoview/pom.xml @@ -18,7 +18,7 @@ org.eclipse.jdt org.eclipse.jdt.bcoview - 1.2.300-SNAPSHOT + 1.2.400-SNAPSHOT eclipse-plugin diff --git a/org.eclipse.jdt.jeview.feature/feature.xml b/org.eclipse.jdt.jeview.feature/feature.xml index a8da88ad333..7c147c341a6 100644 --- a/org.eclipse.jdt.jeview.feature/feature.xml +++ b/org.eclipse.jdt.jeview.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/org.eclipse.jdt.jeview/META-INF/MANIFEST.MF b/org.eclipse.jdt.jeview/META-INF/MANIFEST.MF index d16232991e4..a1b6747e7ea 100644 --- a/org.eclipse.jdt.jeview/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.jeview/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.jdt.jeview Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.jdt.jeview; singleton:=true -Bundle-Version: 1.5.200.qualifier +Bundle-Version: 1.5.300.qualifier Bundle-Activator: org.eclipse.jdt.jeview.JEViewPlugin Bundle-Vendor: %providerName Bundle-Localization: plugin diff --git a/org.eclipse.jdt.junit.core/forceQualifierUpdate.txt b/org.eclipse.jdt.junit.core/forceQualifierUpdate.txt index 0dfeaf38898..d1fe2278f23 100644 --- a/org.eclipse.jdt.junit.core/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.junit.core/forceQualifierUpdate.txt @@ -2,4 +2,5 @@ Bug 530709 - Comparator errors in jdt debug and jdt ui in I20180204-2000 Bug 534597 - Unanticipated comparator errors in I20180511-2000 Bug 527899 [9] Implement JEP 280: Indify String Concatenation -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 \ No newline at end of file diff --git a/org.eclipse.jdt.junit/forceQualifierUpdate.txt b/org.eclipse.jdt.junit/forceQualifierUpdate.txt index cd6dc964501..070ad6d4655 100644 --- a/org.eclipse.jdt.junit/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.junit/forceQualifierUpdate.txt @@ -13,4 +13,5 @@ https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/pull/439 Bug 527899 [9] Implement JEP 280: Indify String Concatenation Bug 527899 [9] Implement JEP 280: Indify String Concatenation https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1781 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1781 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 \ No newline at end of file diff --git a/org.eclipse.jdt.text.tests/forceQualifierUpdate.txt b/org.eclipse.jdt.text.tests/forceQualifierUpdate.txt index d97c68bd954..6a632ce6d37 100644 --- a/org.eclipse.jdt.text.tests/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.text.tests/forceQualifierUpdate.txt @@ -8,4 +8,5 @@ Comparator errors in jdt.ui bundles in Y20200127-1055 Bug 564399 - [Comparator] Comparator Errors in Y-build Y20200617-2350 Comparator errors in jdt.ui bundles in I20210317-0330 Bug 527899 [9] Implement JEP 280: Indify String Concatenation -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 \ No newline at end of file diff --git a/org.eclipse.jdt.ui.tests.refactoring/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui.tests.refactoring/META-INF/MANIFEST.MF index 2bf7bc16adf..7ff5750bc90 100644 --- a/org.eclipse.jdt.ui.tests.refactoring/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.ui.tests.refactoring/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Automatic-Module-Name: org.eclipse.jdt.ui.tests.refactoring Bundle-ManifestVersion: 2 Bundle-Name: %Plugin.name Bundle-SymbolicName: org.eclipse.jdt.ui.tests.refactoring; singleton:=true -Bundle-Version: 3.15.300.qualifier +Bundle-Version: 3.15.400.qualifier Bundle-Activator: org.eclipse.jdt.ui.tests.refactoring.infra.RefactoringTestPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %Plugin.providerName diff --git a/org.eclipse.jdt.ui.tests.refactoring/pom.xml b/org.eclipse.jdt.ui.tests.refactoring/pom.xml index 9abfab53d97..ce41658d729 100644 --- a/org.eclipse.jdt.ui.tests.refactoring/pom.xml +++ b/org.eclipse.jdt.ui.tests.refactoring/pom.xml @@ -19,7 +19,7 @@ org.eclipse.jdt org.eclipse.jdt.ui.tests.refactoring - 3.15.300-SNAPSHOT + 3.15.400-SNAPSHOT eclipse-test-plugin ${project.artifactId} diff --git a/org.eclipse.jdt.ui.unittest.junit/forceQualifierUpdate.txt b/org.eclipse.jdt.ui.unittest.junit/forceQualifierUpdate.txt index d2350b50213..93b39387573 100644 --- a/org.eclipse.jdt.ui.unittest.junit/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.ui.unittest.junit/forceQualifierUpdate.txt @@ -1,4 +1,5 @@ # To force a version qualifier update add the bug here Bug 572789 - Comparator errors in I20210412-1800 after moving to compiler from 4.20 M1 Bug 527899 [9] Implement JEP 280: Indify String Concatenation -https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 \ No newline at end of file +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1659 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 \ No newline at end of file diff --git a/org.eclipse.jdt.ui/forceQualifierUpdate.txt b/org.eclipse.jdt.ui/forceQualifierUpdate.txt index b7a65bf57f1..bfc0f26e00e 100644 --- a/org.eclipse.jdt.ui/forceQualifierUpdate.txt +++ b/org.eclipse.jdt.ui/forceQualifierUpdate.txt @@ -17,4 +17,5 @@ Comparator errors in I20230307-0840 Comparator errors in I20230607-0720 Bug 527899 [9] Implement JEP 280: Indify String Concatenation Comparator errors in I20230906-0400 -Comparator errors in I20231127-0750 \ No newline at end of file +Comparator errors in I20231127-0750 +https://github.com/eclipse-platform/eclipse.platform.releng.aggregator/issues/1923 \ No newline at end of file From ff344b7d85407169bea373df350ca14b0c95ac23 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 21 Mar 2024 19:57:04 -0400 Subject: [PATCH 02/19] Fix o.e.jdt.ui failures due to latest JVM branch merge (#1278) * Fix HierarchicalASTVisitor and it's accompanying test --- .../corext/dom/HierarchicalASTVisitor.java | 29 +++++++++++++++++-- .../core/HierarchicalASTVisitorTest.java | 21 +++++++++++++- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java index 688d3bbd19e..464f97c6a8f 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/dom/HierarchicalASTVisitor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2023 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -115,11 +115,11 @@ public void endVisit(BodyDeclaration node) { //---- Begin AbstractTypeDeclaration Hierarchy --------------------------- public boolean visit(AbstractTypeDeclaration node) { - return visit((BodyDeclaration)node); + return visit((AbstractUnnamedTypeDeclaration)node); } public void endVisit(AbstractTypeDeclaration node) { - endVisit((BodyDeclaration)node); + endVisit((AbstractUnnamedTypeDeclaration)node); } @Override @@ -154,6 +154,29 @@ public void endVisit(TypeDeclaration node) { //---- End AbstractTypeDeclaration Hierarchy --------------------------- + //---- Begin AbstractUnnamedTypeDeclaration Hierarchy --------------------------- + + public boolean visit(AbstractUnnamedTypeDeclaration node) { + return visit((BodyDeclaration)node); + } + + public void endVisit(AbstractUnnamedTypeDeclaration node) { + endVisit((BodyDeclaration)node); + } + + @Override + public boolean visit(UnnamedClass node) { + return visit((AbstractUnnamedTypeDeclaration)node); + } + + @Override + public void endVisit(UnnamedClass node) { + endVisit((AbstractUnnamedTypeDeclaration)node); + } + + //---- End AbstractUnnamedTypeDeclaration Hierarchy --------------------------- + + @Override public boolean visit(AnnotationTypeMemberDeclaration node) { return visit((BodyDeclaration)node); diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/HierarchicalASTVisitorTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/HierarchicalASTVisitorTest.java index 86b744d4b44..68381c35e78 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/HierarchicalASTVisitorTest.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/HierarchicalASTVisitorTest.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2021 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -32,6 +32,7 @@ import org.eclipse.jdt.core.dom.AbstractTagElement; import org.eclipse.jdt.core.dom.AbstractTextElement; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; +import org.eclipse.jdt.core.dom.AbstractUnnamedTypeDeclaration; import org.eclipse.jdt.core.dom.AnnotatableType; import org.eclipse.jdt.core.dom.Annotation; import org.eclipse.jdt.core.dom.BodyDeclaration; @@ -167,6 +168,24 @@ public void superEndVisit(AbstractTextElement node) { super.visit(node); } + @Override + public boolean visit(AbstractUnnamedTypeDeclaration node) { + registerCall(AbstractUnnamedTypeDeclaration.class); + return false; + } + @SuppressWarnings("unused") // called reflectively + public void superVisit(AbstractUnnamedTypeDeclaration node) { + super.visit(node); + } + @Override + public void endVisit(AbstractUnnamedTypeDeclaration node) { + registerCall(AbstractUnnamedTypeDeclaration.class); + } + @SuppressWarnings("unused") // called reflectively + public void superEndVisit(AbstractUnnamedTypeDeclaration node) { + super.visit(node); + } + @Override public boolean visit(Comment node) { registerCall(Comment.class); From 37ef0475021a0431325da3f610081dac2efebd1c Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 22 Mar 2024 00:17:53 -0400 Subject: [PATCH 03/19] Do not remove comments for string concat to text block (#1275) - fix StringConcatToTextBlockFixCore to not convert to text block if any of the concatenated elements have non-empty line comments or block comments - also fix the last line of a text block when formed from a concatenation that ends in a space or tab-character as it still requires escaping - add new tests to CleanUpTest15 - fixes #1273 --- .../fix/StringConcatToTextBlockFixCore.java | 31 +++++++++++++++++-- .../jdt/ui/tests/quickfix/CleanUpTest15.java | 24 ++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java index 81165ca43f8..171795cfb4f 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java @@ -126,7 +126,7 @@ public boolean visit(final InfixExpression visited) { if (!(rightHand instanceof StringLiteral)) { return false; } - StringLiteral rightLiteral= (StringLiteral)leftHand; + StringLiteral rightLiteral= (StringLiteral)rightHand; ICompilationUnit cu= (ICompilationUnit)cUnit.getJavaElement(); hasComments= hasComments || hasNLS(ASTNodes.getTrailingComments(rightLiteral), cu); literal= rightLiteral.getLiteralValue(); @@ -158,7 +158,26 @@ public boolean visit(final InfixExpression visited) { return false; } boolean isTagged= false; - if (hasComments && ASTNodes.getFirstAncestorOrNull(visited, Annotation.class) == null) { + // if there are any block comments or non-empty line comments and we aren't NLS, abandon change + if (!hasComments) { + List comments= ASTNodes.getCommentsForRegion(cUnit, visited.getStartPosition(), visited.getLength()); + if (!comments.isEmpty()) { + IBuffer buffer; + try { + buffer= cu.getBuffer(); + for (Comment comment : comments) { + if (!(comment instanceof LineComment lineComment)) { + return false; + } + if (!buffer.getText(comment.getStartPosition() + 2, comment.getLength() - 2).trim().isEmpty()) { + return false; + } + } + } catch (JavaModelException e) { + // fall through + } + } + } else if (ASTNodes.getFirstAncestorOrNull(visited, Annotation.class) == null) { NLSLine nlsLine= scanCurrentLine(cu, leftHand); if (nlsLine == null) { return false; @@ -335,6 +354,14 @@ public void accept(Expression t) { for (i= count; i > 0; --i) { buf.append("\\\""); //$NON-NLS-1$ } + i= buf.length() - 1; + if (buf.charAt(i) == ' ') { + buf.deleteCharAt(i); + buf.append("\\s"); //$NON-NLS-1$ + } else if (buf.charAt(i) == '\t') { + buf.deleteCharAt(i); + buf.append("\\t"); //$NON-NLS-1$ + } } buf.append("\"\"\""); //$NON-NLS-1$ if (!isTagged) { diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java index 4dcd9c815ae..f977f8e3f96 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java @@ -149,7 +149,13 @@ public void testConcatToTextBlock() throws Exception { + " + \" * foo\\n\" //\n" // + " + \" */\"; //\n" // + " }\n" // - + "}\n"; + + " public void testNoEndNewlineWithSpace() {\n" // + + " String x= \"\"\n" // + + " + \"/** bar\\n\" //\n" // + + " + \" * foo\\n\" //\n" // + + " + \" */ \"; //\n" // + + " }\n" // + + "}\n"; ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null); @@ -266,7 +272,13 @@ public void testConcatToTextBlock() throws Exception { + " */\\\n" // + " \"\"\"; //\n" // + " }\n" // - + "}\n"; + + " public void testNoEndNewlineWithSpace() {\n" // + + " String x= \"\"\"\n" // + + " /** bar\n" // + + " * foo\n" // + + " */\\s\"\"\"; //\n" // + + " }\n" // + + "}\n"; assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 }, null); } @@ -657,6 +669,14 @@ public void testNoConcatToTextBlock() throws Exception { + " \"ghijkl\" + //$NON-NLS-1$\n" // + " \"mnop\"};\n" // + " }\n" // + + "\n" // + + " public void testCommentsThatWillBeLost() {\n" // + + " String x = \"\" +\n" // + + " \"abcdef\" +\n" // + + " \"ghijkl\" + // a comment\n" // + + " \"mnop\";\n" // + + " }\n" // + + "\n" // + "}\n"; ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null); From 2b7d19c69bd126176d9eea63c4b9f8a1cda394c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 22 Mar 2024 09:12:50 +0200 Subject: [PATCH 04/19] Update tycho-build to 4.0.6 --- .mvn/extensions.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/extensions.xml b/.mvn/extensions.xml index d5465e18875..02d68c6004d 100644 --- a/.mvn/extensions.xml +++ b/.mvn/extensions.xml @@ -3,6 +3,6 @@ org.eclipse.tycho tycho-build - 4.0.4 + 4.0.6 From c1f3710be7b0237347d5747fcafad0dfa164286e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 22 Mar 2024 09:16:33 +0200 Subject: [PATCH 05/19] Remove unused api problem filters --- .../.settings/.api_filters | 7 ---- org.eclipse.jdt.ui/.settings/.api_filters | 40 ------------------- 2 files changed, 47 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/.settings/.api_filters b/org.eclipse.jdt.core.manipulation/.settings/.api_filters index 3198459f671..5bf999a9868 100644 --- a/org.eclipse.jdt.core.manipulation/.settings/.api_filters +++ b/org.eclipse.jdt.core.manipulation/.settings/.api_filters @@ -37,13 +37,6 @@ - - - - - - - diff --git a/org.eclipse.jdt.ui/.settings/.api_filters b/org.eclipse.jdt.ui/.settings/.api_filters index 7b3cdd7e8d3..b181190c4ad 100644 --- a/org.eclipse.jdt.ui/.settings/.api_filters +++ b/org.eclipse.jdt.ui/.settings/.api_filters @@ -1,13 +1,5 @@ - - - - - - - - @@ -161,38 +153,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From aba677ef4b51cab2410be124ccf00d516df40fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 22 Mar 2024 09:57:22 +0200 Subject: [PATCH 06/19] Add about.html to source bundles Fixes BuildTests issues in I-builds. --- org.eclipse.jdt.astview/build.properties | 2 +- org.eclipse.jdt.bcoview/build.properties | 2 +- org.eclipse.jdt.jeview/build.properties | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jdt.astview/build.properties b/org.eclipse.jdt.astview/build.properties index 36eeaf017cf..10f15087f53 100644 --- a/org.eclipse.jdt.astview/build.properties +++ b/org.eclipse.jdt.astview/build.properties @@ -17,7 +17,7 @@ bin.includes = plugin.xml,\ plugin.properties,\ META-INF/,\ . - +src.includes = about.html source.. = src/ output.. = bin/ jre.compilation.profile = JavaSE-1.8 diff --git a/org.eclipse.jdt.bcoview/build.properties b/org.eclipse.jdt.bcoview/build.properties index 3e914809394..20b0ceedca8 100644 --- a/org.eclipse.jdt.bcoview/build.properties +++ b/org.eclipse.jdt.bcoview/build.properties @@ -6,7 +6,7 @@ bin.includes = README.md,\ about.html,\ plugin.properties,\ .options - +src.includes = about.html jars.compile.order = . source.. = src/ output.. = bin/ diff --git a/org.eclipse.jdt.jeview/build.properties b/org.eclipse.jdt.jeview/build.properties index 21cfb2a7e3c..08f216877d4 100644 --- a/org.eclipse.jdt.jeview/build.properties +++ b/org.eclipse.jdt.jeview/build.properties @@ -19,6 +19,7 @@ bin.includes = plugin.xml,\ icons/,\ plugin.properties,\ about.html -src.includes = icons/ +src.includes = icons/,\ + about.html javacDefaultEncoding.. = UTF-8 pom.model.groupId = org.eclipse.jdt From 859cbf3efc55932921d65963ce8037cb21ec046a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=8A?= =?UTF-8?q?=D1=80=20=D0=9A=D1=83=D1=80=D1=82=D0=B0=D0=BA=D0=BE=D0=B2?= Date: Fri, 22 Mar 2024 10:00:47 +0200 Subject: [PATCH 07/19] Remove useless jre.compilation.profile from ast view --- org.eclipse.jdt.astview.feature/build.properties | 1 - org.eclipse.jdt.astview/build.properties | 1 - 2 files changed, 2 deletions(-) diff --git a/org.eclipse.jdt.astview.feature/build.properties b/org.eclipse.jdt.astview.feature/build.properties index 3524a1fc7ea..570152d57f6 100644 --- a/org.eclipse.jdt.astview.feature/build.properties +++ b/org.eclipse.jdt.astview.feature/build.properties @@ -15,5 +15,4 @@ bin.includes = feature.xml,\ feature.properties src.includes = feature.xml,\ feature.properties -jre.compilation.profile = J2SE-1.4 pom.model.groupId = org.eclipse.jdt.feature diff --git a/org.eclipse.jdt.astview/build.properties b/org.eclipse.jdt.astview/build.properties index 10f15087f53..a8d3b0a16c6 100644 --- a/org.eclipse.jdt.astview/build.properties +++ b/org.eclipse.jdt.astview/build.properties @@ -20,6 +20,5 @@ bin.includes = plugin.xml,\ src.includes = about.html source.. = src/ output.. = bin/ -jre.compilation.profile = JavaSE-1.8 javacDefaultEncoding.. = UTF-8 pom.model.groupId = org.eclipse.jdt From 3dcac4d1ce5540796481b030e434bb16e2a4bfde Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Fri, 22 Mar 2024 16:13:08 -0400 Subject: [PATCH 08/19] Fix string concat to text block so it does not escape single quotes (#1281) - add new test to CleanUpTest15 - fixes #1240 --- .../fix/StringConcatToTextBlockFixCore.java | 4 +++ .../jdt/ui/tests/quickfix/CleanUpTest15.java | 26 +++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java index 171795cfb4f..a2defbda6fd 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java @@ -432,6 +432,10 @@ private static List unescapeBlock(String escapedText) { transformed.append(escapedText.substring(readIndex, bsIndex)); transformed.append("\t"); //$NON-NLS-1$ readIndex= bsIndex + (escapedText.startsWith("\\t", bsIndex) ? 2 : 7); //$NON-NLS-1$ + } else if (escapedText.startsWith("\\'", bsIndex) || escapedText.startsWith("\\u005c'", bsIndex)) { //$NON-NLS-1$ //$NON-NLS-2$ + transformed.append(escapedText.substring(readIndex, bsIndex)); + transformed.append("'"); //$NON-NLS-1$ + readIndex= bsIndex + (escapedText.startsWith("\\'", bsIndex) ? 2 : 7); //$NON-NLS-1$ } else { transformed.append(escapedText.substring(readIndex, bsIndex)); transformed.append("\\").append(escapedText.charAt(bsIndex + 1)); //$NON-NLS-1$ diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java index f977f8e3f96..2c4c0b86978 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/CleanUpTest15.java @@ -155,7 +155,18 @@ public void testConcatToTextBlock() throws Exception { + " + \" * foo\\n\" //\n" // + " + \" */ \"; //\n" // + " }\n" // - + "}\n"; + + " public void testEscapedSingleQuote() {\n" // + + " String x= \"\"\n" // + + " + \"public class Test {\\n\"\n" // + + " + \" static String C = \\\"\\\\n\\\";\\n\"\n" // + + " + \" \\n\"\n" // + + " + \" public static void main(String[] args) {\\n\"\n" // + + " + \" System.out.print(C.length());\\n\"\n" // + + " + \" System.out.print(C.charAt(0) == \\'\\\\n\\');\\n\"\n" // + + " + \" }\\n\"\n" // + + " + \"}\";\n" // + + " }\n" // + + "}\n"; ICompilationUnit cu1= pack1.createCompilationUnit("E.java", sample, false, null); @@ -278,7 +289,18 @@ public void testConcatToTextBlock() throws Exception { + " * foo\n" // + " */\\s\"\"\"; //\n" // + " }\n" // - + "}\n"; + + " public void testEscapedSingleQuote() {\n" // + + " String x= \"\"\"\n" // + + " public class Test {\n" // + + " static String C = \"\\\\n\";\n" // + + " \\s\n" // + + " public static void main(String[] args) {\n" // + + " System.out.print(C.length());\n" // + + " System.out.print(C.charAt(0) == '\\\\n');\n" // + + " }\n" // + + " }\"\"\";\n" // + + " }\n" // + + "}\n"; assertRefactoringResultAsExpected(new ICompilationUnit[] { cu1 }, new String[] { expected1 }, null); } From de1ec302ff2840faa8e83756f879e8960b8f6284 Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Fri, 22 Mar 2024 16:33:09 -0400 Subject: [PATCH 09/19] Add fAddFinal to insert 'final' modifier to some proposals. - "Create local variable/field" & "Assign to new local variable/field" Signed-off-by: Roland Grunberg --- .../UnresolvedElementsBaseSubProcessor.java | 12 +++++------ .../AssignToVariableAssistProposalCore.java | 19 +++++++++++++++--- .../NewVariableCorrectionProposalCore.java | 20 ++++++++++++++++++- .../AssignToVariableAssistProposal.java | 6 +++--- .../NewVariableCorrectionProposal.java | 2 +- 5 files changed, 45 insertions(+), 14 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsBaseSubProcessor.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsBaseSubProcessor.java index 9ba9eab7aae..75d823bc966 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsBaseSubProcessor.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/ui/text/correction/UnresolvedElementsBaseSubProcessor.java @@ -374,7 +374,7 @@ private void addNewVariableProposals(ICompilationUnit cu, Name node, SimpleName if (type == ASTNode.METHOD_DECLARATION) { int relevance= StubUtility.hasParameterName(cu.getJavaProject(), name) ? IProposalRelevance.CREATE_PARAMETER_PREFIX_OR_SUFFIX_MATCH : IProposalRelevance.CREATE_PARAMETER; String label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_createparameter_description, BasicElementLabels.getJavaElementName(name)); - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.PARAM, simpleName, null, relevance); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.PARAM, simpleName, null, relevance, false); T p= newVariableCorrectionProposalToT(core, NewVariableProposal1); if (p != null) proposals.add(p); @@ -382,7 +382,7 @@ private void addNewVariableProposals(ICompilationUnit cu, Name node, SimpleName if (type == ASTNode.INITIALIZER || type == ASTNode.METHOD_DECLARATION && !ASTResolving.isInsideConstructorInvocation((MethodDeclaration) bodyDeclaration, node)) { int relevance= StubUtility.hasLocalVariableName(cu.getJavaProject(), name) ? IProposalRelevance.CREATE_LOCAL_PREFIX_OR_SUFFIX_MATCH : IProposalRelevance.CREATE_LOCAL; String label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_createlocal_description, BasicElementLabels.getJavaElementName(name)); - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.LOCAL, simpleName, null, relevance); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.LOCAL, simpleName, null, relevance, false); T p= newVariableCorrectionProposalToT(core, NewVariableProposal2); if (p != null) proposals.add(p); @@ -451,7 +451,7 @@ private void addNewFieldForType(ICompilationUnit targetCU, ITypeBinding binding, String label; if (senderDeclBinding.isEnum() && !isWriteAccess) { label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_createenum_description, new Object[] { nameLabel, ASTResolving.getTypeSignature(senderDeclBinding) }); - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.ENUM_CONST, simpleName, senderDeclBinding, 10); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.ENUM_CONST, simpleName, senderDeclBinding, 10, false); T p1= newVariableCorrectionProposalToT(core, NewFieldForTypeProposal1); if (p1 != null) proposals.add(p1); @@ -466,7 +466,7 @@ private void addNewFieldForType(ICompilationUnit targetCU, ITypeBinding binding, label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_createfield_other_description, new Object[] { nameLabel, ASTResolving.getTypeSignature(senderDeclBinding) } ); uid= NewFieldForTypeProposal3; } - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.FIELD, simpleName, senderDeclBinding, fieldRelevance); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.FIELD, simpleName, senderDeclBinding, fieldRelevance, false); T prop= newVariableCorrectionProposalToT(core, uid); if (prop != null) proposals.add(prop); @@ -482,7 +482,7 @@ private void addNewFieldForType(ICompilationUnit targetCU, ITypeBinding binding, uid= NewFieldForTypeProposal5; } int constRelevance= StubUtility.hasConstantName(targetCU.getJavaProject(), name) ? IProposalRelevance.CREATE_CONSTANT_PREFIX_OR_SUFFIX_MATCH : IProposalRelevance.CREATE_CONSTANT; - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.CONST_FIELD, simpleName, senderDeclBinding, constRelevance); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, targetCU, NewVariableCorrectionProposalCore.CONST_FIELD, simpleName, senderDeclBinding, constRelevance, false); T prop= newVariableCorrectionProposalToT(core, uid); if (prop != null) proposals.add(prop); @@ -787,7 +787,7 @@ private SimpleName addEnhancedForWithoutTypeProposals(ICompilationUnit cu, ASTNo String name= simpleName.getIdentifier(); int relevance= StubUtility.hasLocalVariableName(cu.getJavaProject(), name) ? 10 : 7; String label= Messages.format(CorrectionMessages.UnresolvedElementsSubProcessor_create_loop_variable_description, BasicElementLabels.getJavaElementName(name)); - NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.LOCAL, simpleName, null, relevance); + NewVariableCorrectionProposalCore core= new NewVariableCorrectionProposalCore(label, cu, NewVariableCorrectionProposalCore.LOCAL, simpleName, null, relevance, false); T prop= newVariableCorrectionProposalToT(core, EnhancedForWithoutTypeProposal1); if (prop != null) proposals.add(prop); diff --git a/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposalCore.java b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposalCore.java index a8bfe447010..b413a1a7a13 100644 --- a/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposalCore.java +++ b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposalCore.java @@ -113,8 +113,9 @@ public class AssignToVariableAssistProposalCore extends LinkedCorrectionProposal private final List fParamNames; private VariableDeclarationFragment fExistingFragment; + private final boolean fAddFinal; - public AssignToVariableAssistProposalCore(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance) { + public AssignToVariableAssistProposalCore(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance, boolean addFinal) { super("", cu, null, relevance); //$NON-NLS-1$ fCUnit= cu; @@ -122,6 +123,7 @@ public AssignToVariableAssistProposalCore(ICompilationUnit cu, int variableKind, fParamNames = null; fNodesToAssign= new ArrayList<>(); fNodesToAssign.add(node); + fAddFinal= addFinal; fTypeBinding= Bindings.normalizeForDeclarationUse(typeBinding, node.getAST()); if (variableKind == AssignToVariableAssistProposalCore.LOCAL) { @@ -134,7 +136,7 @@ public AssignToVariableAssistProposalCore(ICompilationUnit cu, int variableKind, createImportRewrite((CompilationUnit) node.getRoot()); } - public AssignToVariableAssistProposalCore(ICompilationUnit cu, SingleVariableDeclaration parameter, VariableDeclarationFragment existingFragment, ITypeBinding typeBinding, int relevance) { + public AssignToVariableAssistProposalCore(ICompilationUnit cu, SingleVariableDeclaration parameter, VariableDeclarationFragment existingFragment, ITypeBinding typeBinding, int relevance, boolean addFinal) { super("", cu, null, relevance); //$NON-NLS-1$ fCUnit= cu; @@ -144,6 +146,7 @@ public AssignToVariableAssistProposalCore(ICompilationUnit cu, SingleVariableDec fParamNames= null; fTypeBinding= typeBinding; fExistingFragment= existingFragment; + fAddFinal= addFinal; if (existingFragment == null) { setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assignparamtofield_description); @@ -152,7 +155,7 @@ public AssignToVariableAssistProposalCore(ICompilationUnit cu, SingleVariableDec } } - public AssignToVariableAssistProposalCore(ICompilationUnit cu, List parameters, int relevance) { + public AssignToVariableAssistProposalCore(ICompilationUnit cu, List parameters, int relevance, boolean addFinal) { super("", cu, null, relevance); //$NON-NLS-1$ fCUnit= cu; @@ -161,6 +164,7 @@ public AssignToVariableAssistProposalCore(ICompilationUnit cu, List(); + fAddFinal = addFinal; populateNames(parameters); setDisplayName(CorrectionMessages.AssignToVariableAssistProposal_assignallparamstofields_description); } @@ -224,11 +228,17 @@ private ASTRewrite doAddLocal() throws CoreException { if (needsSemicolon(expression)) { VariableDeclarationStatement varStatement= ast.newVariableDeclarationStatement(newDeclFrag); + if (fAddFinal) { + varStatement.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } varStatement.setType(type); rewrite.replace(expression, varStatement, null); } else { // trick for bug 43248: use an VariableDeclarationExpression and keep the ExpressionStatement VariableDeclarationExpression varExpression= ast.newVariableDeclarationExpression(newDeclFrag); + if (fAddFinal) { + varExpression.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } varExpression.setType(type); rewrite.replace(expression, varExpression, null); } @@ -389,6 +399,9 @@ private ASTRewrite doAddField(ASTRewrite rewrite, ASTNode nodeToAssign, ITypeBin boolean isStatic= Modifier.isStatic(bodyDecl.getModifiers()) && !isAnonymous; boolean isConstructorParam= isParamToField && nodeToAssign.getParent() instanceof MethodDeclaration && ((MethodDeclaration) nodeToAssign.getParent()).isConstructor(); int modifiers= Modifier.PRIVATE; + if (fAddFinal) { + modifiers |= Modifier.FINAL; + } if (isStatic) { modifiers |= Modifier.STATIC; } else if (isConstructorParam) { diff --git a/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposalCore.java b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposalCore.java index 5ceb6dc3d8c..d5f0356af96 100644 --- a/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposalCore.java +++ b/org.eclipse.jdt.core.manipulation/proposals/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposalCore.java @@ -90,8 +90,9 @@ public class NewVariableCorrectionProposalCore extends LinkedCorrectionProposalC final private int fVariableKind; final private SimpleName fOriginalNode; final private ITypeBinding fSenderBinding; + final private boolean fAddFinal; - public NewVariableCorrectionProposalCore(String label, ICompilationUnit cu, int variableKind, SimpleName node, ITypeBinding senderBinding, int relevance) { + public NewVariableCorrectionProposalCore(String label, ICompilationUnit cu, int variableKind, SimpleName node, ITypeBinding senderBinding, int relevance, boolean addFinal) { super(label, cu, null, relevance); if (senderBinding == null) { Assert.isTrue(variableKind == PARAM || variableKind == LOCAL); @@ -102,6 +103,7 @@ public NewVariableCorrectionProposalCore(String label, ICompilationUnit cu, int fVariableKind= variableKind; fOriginalNode= node; fSenderBinding= senderBinding; + fAddFinal = addFinal; } @Override @@ -264,6 +266,9 @@ private ASTRewrite doAddLocal(CompilationUnit cu) { // and replace the assignment with an VariableDeclarationExpression VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment(); VariableDeclarationExpression newDecl= ast.newVariableDeclarationExpression(newDeclFrag); + if (fAddFinal) { + newDecl.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } newDecl.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext, TypeLocation.LOCAL_VARIABLE)); Expression placeholder= (Expression) rewrite.createCopyTarget(assignment.getRightHandSide()); @@ -289,6 +294,10 @@ private ASTRewrite doAddLocal(CompilationUnit cu) { frag.setInitializer(placeholder); expression.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext, TypeLocation.LOCAL_VARIABLE)); + if (fAddFinal) { + expression.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } + rewrite.replace(assignment, expression, null); addLinkedPosition(rewrite.track(expression.getType()), false, KEY_TYPE); @@ -303,6 +312,9 @@ private ASTRewrite doAddLocal(CompilationUnit cu) { EnhancedForStatement enhancedForStatement= (EnhancedForStatement) dominantStatement; SingleVariableDeclaration parameter= enhancedForStatement.getParameter(); + if (fAddFinal) { + parameter.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } Expression expression= enhancedForStatement.getExpression(); SimpleName newName= (SimpleName) rewrite.createMoveTarget(node); @@ -348,6 +360,9 @@ private ASTRewrite doAddLocal(CompilationUnit cu) { newDeclFrag.setName(ast.newSimpleName(node.getIdentifier())); newDecl.setType(evaluateVariableType(ast, imports, importRewriteContext, targetContext, TypeLocation.LOCAL_VARIABLE)); + if (fAddFinal) { + newDecl.modifiers().addAll(ast.newModifiers(Modifier.FINAL)); + } // newDeclFrag.setInitializer(ASTNodeFactory.newDefaultExpression(ast, newDecl.getType(), 0)); addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE); @@ -548,6 +563,9 @@ private int evaluateFieldModifiers(ASTNode newTypeDecl) { } int modifiers= 0; + if (fAddFinal) { + modifiers |= Modifier.FINAL; + } if (fVariableKind == CONST_FIELD) { modifiers |= Modifier.FINAL | Modifier.STATIC; } else { diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java index 7ca6a44e86a..681372b5950 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/AssignToVariableAssistProposal.java @@ -50,7 +50,7 @@ public class AssignToVariableAssistProposal extends LinkedCorrectionProposal { static final String VAR_TYPE= "var"; //$NON-NLS-1$ public AssignToVariableAssistProposal(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance) { - super("", cu, null, relevance, null, new AssignToVariableAssistProposalCore(cu, variableKind, node, typeBinding, relevance)); //$NON-NLS-1$ + super("", cu, null, relevance, null, new AssignToVariableAssistProposalCore(cu, variableKind, node, typeBinding, relevance, false)); //$NON-NLS-1$ if (variableKind == LOCAL) { setImage(JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL)); } else if (variableKind == FIELD) { @@ -61,11 +61,11 @@ public AssignToVariableAssistProposal(ICompilationUnit cu, int variableKind, Exp } public AssignToVariableAssistProposal(ICompilationUnit cu, SingleVariableDeclaration parameter, VariableDeclarationFragment existingFragment, ITypeBinding typeBinding, int relevance) { - super("", cu, null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE), new AssignToVariableAssistProposalCore(cu, parameter, existingFragment, typeBinding, relevance)); //$NON-NLS-1$ + super("", cu, null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE), new AssignToVariableAssistProposalCore(cu, parameter, existingFragment, typeBinding, relevance, false)); //$NON-NLS-1$ } public AssignToVariableAssistProposal(ICompilationUnit cu, List parameters, int relevance) { - super("", cu, null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE), new AssignToVariableAssistProposalCore(cu, parameters, relevance)); //$NON-NLS-1$ + super("", cu, null, relevance, JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE), new AssignToVariableAssistProposalCore(cu, parameters, relevance, false)); //$NON-NLS-1$ } protected LinkedProposalModelCore createProposalModel() { diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java index 6f97f5881ee..9bb26dadc9d 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/correction/proposals/NewVariableCorrectionProposal.java @@ -37,7 +37,7 @@ public NewVariableCorrectionProposal(NewVariableCorrectionProposalCore core, Ima } public NewVariableCorrectionProposal(String label, ICompilationUnit cu, int variableKind, SimpleName node, ITypeBinding senderBinding, int relevance, Image image) { - super(label, cu, null, relevance, image, new NewVariableCorrectionProposalCore(label, cu, variableKind, node, senderBinding, relevance)); + super(label, cu, null, relevance, image, new NewVariableCorrectionProposalCore(label, cu, variableKind, node, senderBinding, relevance, false)); } public int getVariableKind() { From 419433831dd7437003199f4257aab6bd876b1271 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Mon, 25 Mar 2024 20:54:55 -0400 Subject: [PATCH 10/19] Do not offer string concat to text block if a variable is used (#1285) - fix StringConcatToTextBlockFixCore.StringConcatFinder when visiting an InfixExpression, ensure it is not considered part of another InfixExpression implying that all items are not StringLiterals - add new test to AssistQuickFixTest15 - fixes #1284 --- .../fix/StringConcatToTextBlockFixCore.java | 4 ++ .../tests/quickfix/AssistQuickFixTest15.java | 37 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java index a2defbda6fd..027598e8ef9 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/StringConcatToTextBlockFixCore.java @@ -107,6 +107,10 @@ public boolean visit(final InfixExpression visited) { || visited.extendedOperands().isEmpty()) { return false; } + if (visited.getLocationInParent() == InfixExpression.LEFT_OPERAND_PROPERTY || + visited.getLocationInParent() == InfixExpression.RIGHT_OPERAND_PROPERTY) { + return false; + } ITypeBinding typeBinding= visited.resolveTypeBinding(); if (typeBinding == null || !typeBinding.getQualifiedName().equals(JAVA_STRING)) { return false; diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest15.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest15.java index 35555cdfe09..56ae3201f88 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest15.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest15.java @@ -961,5 +961,42 @@ public void testNoConcatToTextBlock9() throws Exception { assertProposalDoesNotExist(proposals, FixMessages.StringConcatToTextBlockFix_convert_msg); } + + @Test + public void testNoConcatToTextBlock10() throws Exception { + fJProject1= JavaProjectHelper.createJavaProject("TestProject1", "bin"); + fJProject1.setRawClasspath(projectSetup.getDefaultClasspath(), null); + JavaProjectHelper.set15CompilerOptions(fJProject1, false); + fSourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src"); + + StringBuilder buf= new StringBuilder(); + buf.append("module test {\n"); + buf.append("}\n"); + IPackageFragment def= fSourceFolder.createPackageFragment("", false, null); + def.createCompilationUnit("module-info.java", buf.toString(), false, null); + + IPackageFragment pack= fSourceFolder.createPackageFragment("test", false, null); + buf= new StringBuilder(); + buf.append("package test;\n"); + buf.append("public class Cls {\n"); + buf.append(" public void inconsistentNLS() {\n"); + buf.append(" // comment 1\n"); + buf.append(" String y = \"something\";\n"); + buf.append(" String x = \"\" +\n"); + buf.append(" \"public void foo() {\\n\" +\n"); + buf.append(" \" System.out.println(\\\"abc\\\");\\n\" +\n"); + buf.append(" y + \n"); + buf.append(" \"}\\n\"; //$NON-NLS-1$ // comment 2\n"); + buf.append(" }\n"); + buf.append("}\n"); + ICompilationUnit cu= pack.createCompilationUnit("Cls.java", buf.toString(), false, null); + + int index= buf.indexOf("x"); + IInvocationContext ctx= getCorrectionContext(cu, index, 1); + assertNoErrors(ctx); + ArrayList proposals= collectAssists(ctx, false); + + assertProposalDoesNotExist(proposals, FixMessages.StringConcatToTextBlockFix_convert_msg); + } } From 0b3382e7940eeb68a95c134c1ee90f1901b04dcd Mon Sep 17 00:00:00 2001 From: Jozef Tomek <36334098+RedeemerSK@users.noreply.github.com> Date: Tue, 26 Mar 2024 20:21:06 +0100 Subject: [PATCH 11/19] Add styling enhancements to signatures (JavaElementLinks) in Javadoc. Fixes #1073 (#1074) * Add styling enhancements to JavaElementLinks in Javadoc (#1073) - fixes #1073 --- .../JavaElementLabelComposerCore.java | 456 ++++++++------ .../manipulation/JavaElementLabelsCore.java | 2 +- org.eclipse.jdt.ui/JavadocHoverStyleSheet.css | 28 +- org.eclipse.jdt.ui/JavadocViewStyleSheet.css | 26 + .../css/e4-dark_jdt_syntaxhighlighting.css | 1 + .../jdt/internal/ui/JavaPluginImages.java | 4 + .../internal/ui/infoviews/JavadocView.java | 41 +- .../ui/text/java/hover/JavadocHover.java | 61 +- .../ArmListeningMenuItemsConfigurer.java | 123 ++++ .../BindingLinkedLabelComposer.java | 14 +- .../ui/viewsupport/JavaElementLinks.java | 588 +++++++++++++++++- .../MenuVisibilityMenuItemsConfigurer.java | 89 +++ .../browser/BrowserTextAccessor.java | 84 +++ .../javadoc/JavadocStylingMessages.java | 43 ++ .../javadoc/JavadocStylingMessages.properties | 22 + ...gnatureStylingColorPreferenceMenuItem.java | 85 +++ .../SignatureStylingColorSubMenuItem.java | 153 +++++ .../SignatureStylingMenuToolbarAction.java | 193 ++++++ .../eclipse/jdt/ui/PreferenceConstants.java | 4 + 19 files changed, 1800 insertions(+), 217 deletions(-) create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/ArmListeningMenuItemsConfigurer.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/MenuVisibilityMenuItemsConfigurer.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/browser/BrowserTextAccessor.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.properties create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorPreferenceMenuItem.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorSubMenuItem.java create mode 100644 org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingMenuToolbarAction.java diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java index b8cd1c2df55..6775c7326fc 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java @@ -249,202 +249,40 @@ public void appendMethodLabel(IMethod method, long flags) { // type parameters if (getFlag(flags, JavaElementLabelsCore.M_PRE_TYPE_PARAMETERS)) { - if (resolvedKey != null) { - if (resolvedKey.isParameterizedMethod()) { - String[] typeArgRefs= resolvedKey.getTypeArguments(); - if (typeArgRefs.length > 0) { - appendTypeArgumentSignaturesLabel(method, typeArgRefs, flags); - fBuffer.append(' '); - } - } else { - String[] typeParameterSigs= Signature.getTypeParameters(resolvedSig); - if (typeParameterSigs.length > 0) { - appendTypeParameterSignaturesLabel(typeParameterSigs, flags); - fBuffer.append(' '); - } - } - } else if (method.exists()) { - ITypeParameter[] typeParameters= method.getTypeParameters(); - if (typeParameters.length > 0) { - appendTypeParametersLabels(typeParameters, flags); - fBuffer.append(' '); - } - } + appendMethodPrependedTypeParams(method, flags, resolvedKey, resolvedSig); } // return type if (getFlag(flags, JavaElementLabelsCore.M_PRE_RETURNTYPE) && method.exists() && !method.isConstructor()) { - String returnTypeSig= resolvedSig != null ? Signature.getReturnType(resolvedSig) : method.getReturnType(); - appendTypeSignatureLabel(method, returnTypeSig, flags); - fBuffer.append(' '); + appendMethodPrependedReturnType(method, flags, resolvedSig); } // qualification if (getFlag(flags, JavaElementLabelsCore.M_FULLY_QUALIFIED)) { - appendTypeLabel(method.getDeclaringType(), JavaElementLabelsCore.T_FULLY_QUALIFIED | (flags & QUALIFIER_FLAGS)); - fBuffer.append('.'); + appendMethodQualification(method, flags); } - fBuffer.append(getElementName(method)); + appendMethodName(method); // constructor type arguments if (getFlag(flags, JavaElementLabelsCore.T_TYPE_PARAMETERS) && method.exists() && method.isConstructor()) { - if (resolvedKey != null && resolvedSig != null && resolvedKey.isParameterizedType()) { - BindingKey declaringType= resolvedKey.getDeclaringType(); - if (declaringType != null) { - String[] declaringTypeArguments= declaringType.getTypeArguments(); - appendTypeArgumentSignaturesLabel(method, declaringTypeArguments, flags); - } - } + appendMethodConstructorTypeParams(method, flags, resolvedKey, resolvedSig); } // parameters - fBuffer.append('('); - String[] declaredParameterTypes= method.getParameterTypes(); - if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_TYPES | JavaElementLabelsCore.M_PARAMETER_NAMES)) { - String[] types= null; - int nParams= 0; - boolean renderVarargs= false; - boolean isPolymorphic= false; - if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_TYPES)) { - if (resolvedSig != null) { - types= Signature.getParameterTypes(resolvedSig); - } else { - types= declaredParameterTypes; - } - nParams= types.length; - renderVarargs= method.exists() && Flags.isVarargs(method.getFlags()); - if (renderVarargs - && resolvedSig != null - && declaredParameterTypes.length == 1 - && JavaModelUtil.isPolymorphicSignature(method)) { - renderVarargs= false; - isPolymorphic= true; - } - } - String[] names= null; - if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_NAMES) && method.exists()) { - names= method.getParameterNames(); - if (isPolymorphic) { - // handled specially below - } else if (types == null) { - nParams= names.length; - } else { // types != null - if (nParams != names.length) { - if (resolvedSig != null && types.length > names.length) { - // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=99137 - nParams= names.length; - String[] typesWithoutSyntheticParams= new String[nParams]; - System.arraycopy(types, types.length - nParams, typesWithoutSyntheticParams, 0, nParams); - types= typesWithoutSyntheticParams; - } else { - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=101029 - // JavaPlugin.logErrorMessage("JavaElementLabels: Number of param types(" + nParams + ") != number of names(" + names.length + "): " + method.getElementName()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ - names= null; // no names rendered - } - } - } - } - - ILocalVariable[] annotatedParameters= null; - if (nParams > 0 && getFlag(flags, JavaElementLabelsCore.M_PARAMETER_ANNOTATIONS)) { - annotatedParameters= method.getParameters(); - } - - for (int i= 0; i < nParams; i++) { - if (i > 0) { - fBuffer.append(JavaElementLabelsCore.COMMA_STRING); - } - if (annotatedParameters != null && i < annotatedParameters.length) { - appendAnnotationLabels(annotatedParameters[i].getAnnotations(), flags); - } - - if (types != null) { - String paramSig= types[i]; - if (renderVarargs && (i == nParams - 1)) { - int newDim= Signature.getArrayCount(paramSig) - 1; - appendTypeSignatureLabel(method, Signature.getElementType(paramSig), flags); - for (int k= 0; k < newDim; k++) { - fBuffer.append('[').append(']'); - } - fBuffer.append(JavaElementLabelsCore.ELLIPSIS_STRING); - } else { - appendTypeSignatureLabel(method, paramSig, flags); - } - } - if (names != null) { - if (types != null) { - fBuffer.append(' '); - } - if (isPolymorphic) { - fBuffer.append(names[0] + i); - } else { - fBuffer.append(names[i]); - } - } - } - } else { - if (declaredParameterTypes.length > 0) { - fBuffer.append(JavaElementLabelsCore.ELLIPSIS_STRING); - } - } - fBuffer.append(')'); + appendMethodParams(method, flags, resolvedSig); if (getFlag(flags, JavaElementLabelsCore.M_EXCEPTIONS)) { - String[] types; - if (resolvedKey != null) { - types= resolvedKey.getThrownExceptions(); - } else { - types= method.exists() ? method.getExceptionTypes() : new String[0]; - } - if (types.length > 0) { - fBuffer.append(" throws "); //$NON-NLS-1$ - for (int i= 0; i < types.length; i++) { - if (i > 0) { - fBuffer.append(JavaElementLabelsCore.COMMA_STRING); - } - appendTypeSignatureLabel(method, types[i], flags); - } - } + appendMethodExceptions(method, flags, resolvedKey); } if (getFlag(flags, JavaElementLabelsCore.M_APP_TYPE_PARAMETERS)) { - int offset= fBuffer.length(); - if (resolvedKey != null) { - if (resolvedKey.isParameterizedMethod()) { - String[] typeArgRefs= resolvedKey.getTypeArguments(); - if (typeArgRefs.length > 0) { - fBuffer.append(' '); - appendTypeArgumentSignaturesLabel(method, typeArgRefs, flags); - } - } else { - String[] typeParameterSigs= Signature.getTypeParameters(resolvedSig); - if (typeParameterSigs.length > 0) { - fBuffer.append(' '); - appendTypeParameterSignaturesLabel(typeParameterSigs, flags); - } - } - } else if (method.exists()) { - ITypeParameter[] typeParameters= method.getTypeParameters(); - if (typeParameters.length > 0) { - fBuffer.append(' '); - appendTypeParametersLabels(typeParameters, flags); - } - } - if (getFlag(flags, JavaElementLabelsCore.COLORIZE) && offset != fBuffer.length()) { - setDecorationsStyle(offset); - } + appendMethodAppendedTypeParams(method, flags, resolvedKey, resolvedSig); } if (getFlag(flags, JavaElementLabelsCore.M_APP_RETURNTYPE) && method.exists() && !method.isConstructor()) { - int offset= fBuffer.length(); - fBuffer.append(JavaElementLabelsCore.DECL_STRING); - String returnTypeSig= resolvedSig != null ? Signature.getReturnType(resolvedSig) : method.getReturnType(); - appendTypeSignatureLabel(method, returnTypeSig, flags); - if (getFlag(flags, JavaElementLabelsCore.COLORIZE)) { - setDecorationsStyle(offset); - } + appendMethodAppendedReturnType(method, flags, resolvedSig); } // category @@ -453,12 +291,7 @@ public void appendMethodLabel(IMethod method, long flags) { // post qualification if (getFlag(flags, JavaElementLabelsCore.M_POST_QUALIFIED)) { - int offset= fBuffer.length(); - fBuffer.append(JavaElementLabelsCore.CONCAT_STRING); - appendTypeLabel(method.getDeclaringType(), JavaElementLabelsCore.T_FULLY_QUALIFIED | (flags & QUALIFIER_FLAGS)); - if (getFlag(flags, JavaElementLabelsCore.COLORIZE)) { - setQualifierStyle(offset); - } + appendMethodPostQualification(method, flags); } } catch (JavaModelException e) { @@ -470,6 +303,230 @@ public void appendMethodLabel(IMethod method, long flags) { } } + protected void appendMethodPrependedTypeParams(IMethod method, long flags, BindingKey resolvedKey, String resolvedSignature) throws JavaModelException { + if (resolvedKey != null) { + if (resolvedKey.isParameterizedMethod()) { + String[] typeArgRefs= resolvedKey.getTypeArguments(); + if (typeArgRefs.length > 0) { + appendTypeArgumentSignaturesLabel(method, typeArgRefs, flags); + fBuffer.append(' '); + } + } else { + String[] typeParameterSigs= Signature.getTypeParameters(resolvedSignature); + if (typeParameterSigs.length > 0) { + appendTypeParameterSignaturesLabel(typeParameterSigs, flags); + fBuffer.append(' '); + } + } + } else if (method.exists()) { + ITypeParameter[] typeParameters= method.getTypeParameters(); + if (typeParameters.length > 0) { + appendTypeParametersLabels(typeParameters, flags); + fBuffer.append(' '); + } + } + } + + protected void appendMethodPrependedReturnType(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + String returnTypeSig= resolvedSignature != null ? Signature.getReturnType(resolvedSignature) : method.getReturnType(); + appendMethodParamTypeSignature(method, flags, returnTypeSig); + fBuffer.append(' '); + } + + protected void appendMethodName(IMethod method) { + fBuffer.append(getElementName(method)); + } + + protected void appendMethodQualification(IMethod method, long flags) { + appendTypeLabel(method.getDeclaringType(), JavaElementLabelsCore.T_FULLY_QUALIFIED | (flags & QUALIFIER_FLAGS)); + fBuffer.append('.'); + } + + protected void appendMethodConstructorTypeParams(IMethod method, long flags, BindingKey resolvedKey, String resolvedSignature) { + if (resolvedKey != null && resolvedSignature != null && resolvedKey.isParameterizedType()) { + BindingKey declaringType= resolvedKey.getDeclaringType(); + if (declaringType != null) { + String[] declaringTypeArguments= declaringType.getTypeArguments(); + appendTypeArgumentSignaturesLabel(method, declaringTypeArguments, flags); + } + } + } + + protected void appendMethodParams(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + fBuffer.append('('); + if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_TYPES | JavaElementLabelsCore.M_PARAMETER_NAMES)) { + appendMethodParamsList(method, flags, resolvedSignature); + } else { + if (method.getParameterTypes().length > 0) { + fBuffer.append(JavaElementLabelsCore.ELLIPSIS_STRING); + } + } + fBuffer.append(')'); + } + + protected void appendMethodParamsList(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + String[] declaredParameterTypes= method.getParameterTypes(); + String[] types= null; + int nParams= 0; + boolean renderVarargs= false; + boolean isPolymorphic= false; + if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_TYPES)) { + if (resolvedSignature != null) { + types= Signature.getParameterTypes(resolvedSignature); + } else { + types= declaredParameterTypes; + } + nParams= types.length; + renderVarargs= method.exists() && Flags.isVarargs(method.getFlags()); + if (renderVarargs + && resolvedSignature != null + && declaredParameterTypes.length == 1 + && JavaModelUtil.isPolymorphicSignature(method)) { + renderVarargs= false; + isPolymorphic= true; + } + } + String[] names= null; + if (getFlag(flags, JavaElementLabelsCore.M_PARAMETER_NAMES) && method.exists()) { + names= method.getParameterNames(); + if (isPolymorphic) { + // handled specially below + } else if (types == null) { + nParams= names.length; + } else { // types != null + if (nParams != names.length) { + if (resolvedSignature != null && types.length > names.length) { + // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=99137 + nParams= names.length; + String[] typesWithoutSyntheticParams= new String[nParams]; + System.arraycopy(types, types.length - nParams, typesWithoutSyntheticParams, 0, nParams); + types= typesWithoutSyntheticParams; + } else { + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=101029 + // JavaPlugin.logErrorMessage("JavaElementLabels: Number of param types(" + nParams + ") != number of names(" + names.length + "): " + method.getElementName()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ + names= null; // no names rendered + } + } + } + } + + ILocalVariable[] annotatedParameters= null; + if (nParams > 0 && getFlag(flags, JavaElementLabelsCore.M_PARAMETER_ANNOTATIONS)) { + annotatedParameters= method.getParameters(); + } + + for (int i= 0; i < nParams; i++) { + IAnnotation[] annotations= null; + if (annotatedParameters != null && i < annotatedParameters.length) { + annotations= annotatedParameters[i].getAnnotations(); + } + String type= types == null ? null : types[i]; + String name= names == null ? null : (isPolymorphic ? names[0] + i : names[i]); + appendMethodParam(method, flags, annotations, type, name, renderVarargs, (i == nParams - 1)); + } + } + + protected void appendMethodParam(IMethod method, long flags, IAnnotation[] annotations, String paramSignature, String name, boolean renderVarargs, boolean isLast) throws JavaModelException { + if (annotations != null) { + appendAnnotationLabels(annotations, flags); + } + + if (paramSignature != null) { + if (renderVarargs && isLast) { + int newDim= Signature.getArrayCount(paramSignature) - 1; + appendMethodParamTypeSignature(method, flags, Signature.getElementType(paramSignature)); + for (int k= 0; k < newDim; k++) { + fBuffer.append('[').append(']'); + } + fBuffer.append(JavaElementLabelsCore.ELLIPSIS_STRING); + } else { + appendMethodParamTypeSignature(method, flags, paramSignature); + } + } + if (name != null) { + if (paramSignature != null) { + fBuffer.append(' '); + } + appendMethodParamName(name); + } + if (!isLast) { + fBuffer.append(JavaElementLabelsCore.COMMA_STRING); + } + } + + protected void appendMethodParamTypeSignature(IMethod method, long flags, String paramSignature) { + appendTypeSignatureLabel(method, paramSignature, flags); + } + + protected void appendMethodParamName(String name) { + fBuffer.append(name); + } + + protected void appendMethodExceptions(IMethod method, long flags, BindingKey resolvedKey) throws JavaModelException { + String[] types; + if (resolvedKey != null) { + types= resolvedKey.getThrownExceptions(); + } else { + types= method.exists() ? method.getExceptionTypes() : new String[0]; + } + if (types.length > 0) { + fBuffer.append(" throws "); //$NON-NLS-1$ + for (int i= 0; i < types.length; i++) { + if (i > 0) { + fBuffer.append(JavaElementLabelsCore.COMMA_STRING); + } + appendTypeSignatureLabel(method, types[i], flags); + } + } + } + + protected void appendMethodAppendedTypeParams(IMethod method, long flags, BindingKey resolvedKey, String resolvedSignature) throws JavaModelException { + int offset= fBuffer.length(); + if (resolvedKey != null) { + if (resolvedKey.isParameterizedMethod()) { + String[] typeArgRefs= resolvedKey.getTypeArguments(); + if (typeArgRefs.length > 0) { + fBuffer.append(' '); + appendTypeArgumentSignaturesLabel(method, typeArgRefs, flags); + } + } else { + String[] typeParameterSigs= Signature.getTypeParameters(resolvedSignature); + if (typeParameterSigs.length > 0) { + fBuffer.append(' '); + appendTypeParameterSignaturesLabel(typeParameterSigs, flags); + } + } + } else if (method.exists()) { + ITypeParameter[] typeParameters= method.getTypeParameters(); + if (typeParameters.length > 0) { + fBuffer.append(' '); + appendTypeParametersLabels(typeParameters, flags); + } + } + if (getFlag(flags, JavaElementLabelsCore.COLORIZE) && offset != fBuffer.length()) { + setDecorationsStyle(offset); + } + } + + protected void appendMethodAppendedReturnType(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + int offset= fBuffer.length(); + fBuffer.append(JavaElementLabelsCore.DECL_STRING); + String returnTypeSig= resolvedSignature != null ? Signature.getReturnType(resolvedSignature) : method.getReturnType(); + appendMethodParamTypeSignature(method, flags, returnTypeSig); + if (getFlag(flags, JavaElementLabelsCore.COLORIZE)) { + setDecorationsStyle(offset); + } + } + + protected void appendMethodPostQualification(IMethod method, long flags) { + int offset= fBuffer.length(); + fBuffer.append(JavaElementLabelsCore.CONCAT_STRING); + appendTypeLabel(method.getDeclaringType(), JavaElementLabelsCore.T_FULLY_QUALIFIED | (flags & QUALIFIER_FLAGS)); + if (getFlag(flags, JavaElementLabelsCore.COLORIZE)) { + setQualifierStyle(offset); + } + } + @SuppressWarnings("unused") protected void appendCategoryLabel(IMember member, long flags) throws JavaModelException { // core does not implement this @@ -558,16 +615,16 @@ public void appendAnnotationValue(IAnnotation annotation, Object value, int valu * @param flags flags with render options * @throws JavaModelException ... */ - private void appendTypeParametersLabels(ITypeParameter[] typeParameters, long flags) throws JavaModelException { + protected void appendTypeParametersLabels(ITypeParameter[] typeParameters, long flags) throws JavaModelException { if (typeParameters.length > 0) { - fBuffer.append(getLT()); + appendLT(); for (int i = 0; i < typeParameters.length; i++) { if (i > 0) { fBuffer.append(JavaElementLabelsCore.COMMA_STRING); } appendTypeParameterWithBounds(typeParameters[i], flags); } - fBuffer.append(getGT()); + appendGT(); } } @@ -685,7 +742,7 @@ public void appendTypeParameterLabel(ITypeParameter typeParameter, long flags) { } } - private void appendTypeParameterWithBounds(ITypeParameter typeParameter, long flags) throws JavaModelException { + protected void appendTypeParameterWithBounds(ITypeParameter typeParameter, long flags) throws JavaModelException { fBuffer.append(getElementName(typeParameter)); if (typeParameter.exists()) { @@ -756,11 +813,9 @@ protected void appendTypeSignatureLabel(IJavaElement enclosingElement, String ty fBuffer.append('?'); } else { if (ch == Signature.C_EXTENDS) { - fBuffer.append("? extends "); //$NON-NLS-1$ - appendTypeSignatureLabel(enclosingElement, typeSig.substring(1), flags); + appendWildcardTypeSignature("? extends ", enclosingElement, typeSig.substring(1), flags); //$NON-NLS-1$ } else if (ch == Signature.C_SUPER) { - fBuffer.append("? super "); //$NON-NLS-1$ - appendTypeSignatureLabel(enclosingElement, typeSig.substring(1), flags); + appendWildcardTypeSignature("? super ", enclosingElement, typeSig.substring(1), flags); //$NON-NLS-1$ } } break; @@ -780,6 +835,11 @@ protected void appendTypeSignatureLabel(IJavaElement enclosingElement, String ty } } + protected void appendWildcardTypeSignature(String prefix, IJavaElement enclosingElement, String typeSignature, long flags) { + fBuffer.append(prefix); + appendTypeSignatureLabel(enclosingElement, typeSignature, flags); + } + private void appendTypeBoundsSignaturesLabel(IJavaElement enclosingElement, String[] typeArgsSig, long flags, boolean isIntersection) { for (int i = 0; i < typeArgsSig.length; i++) { if (i > 0) { @@ -818,16 +878,16 @@ protected String getMemberName(IJavaElement enclosingElement, String typeName, S return memberName; } - private void appendTypeArgumentSignaturesLabel(IJavaElement enclosingElement, String[] typeArgsSig, long flags) { + protected void appendTypeArgumentSignaturesLabel(IJavaElement enclosingElement, String[] typeArgsSig, long flags) { if (typeArgsSig.length > 0) { - fBuffer.append(getLT()); + appendLT(); for (int i = 0; i < typeArgsSig.length; i++) { if (i > 0) { fBuffer.append(JavaElementLabelsCore.COMMA_STRING); } appendTypeSignatureLabel(enclosingElement, typeArgsSig[i], flags); } - fBuffer.append(getGT()); + appendGT(); } } @@ -837,19 +897,26 @@ private void appendTypeArgumentSignaturesLabel(IJavaElement enclosingElement, St * @param typeParamSigs the type parameter signature * @param flags flags with render options */ - private void appendTypeParameterSignaturesLabel(String[] typeParamSigs, long flags) { + protected void appendTypeParameterSignaturesLabel(String[] typeParamSigs, long flags) { if (typeParamSigs.length > 0) { - fBuffer.append(getLT()); + appendLT(); for (int i = 0; i < typeParamSigs.length; i++) { if (i > 0) { fBuffer.append(JavaElementLabelsCore.COMMA_STRING); } fBuffer.append(Signature.getTypeVariable(typeParamSigs[i])); } - fBuffer.append(getGT()); + appendGT(); } } + /** + * Appends the string for rendering the '<' character. + */ + protected void appendLT() { + fBuffer.append(getLT()); + } + /** * Returns the string for rendering the '<' character. * @@ -859,6 +926,13 @@ protected String getLT() { return "<"; //$NON-NLS-1$ } + /** + * Appends the string for rendering the '>' character. + */ + protected void appendGT() { + fBuffer.append(getGT()); + } + /** * Returns the string for rendering the '>' character. * diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelsCore.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelsCore.java index 236f7d59ce6..12036850cbb 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelsCore.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelsCore.java @@ -89,7 +89,7 @@ public final class JavaElementLabelsCore { public final static long M_APP_RETURNTYPE= 1L << 5; /** - * Method names contain return type (appended) + * Method names contain return type (prepended) * e.g. int foo */ public final static long M_PRE_RETURNTYPE= 1L << 6; diff --git a/org.eclipse.jdt.ui/JavadocHoverStyleSheet.css b/org.eclipse.jdt.ui/JavadocHoverStyleSheet.css index ff82b2b6f19..bc2b0c31d67 100644 --- a/org.eclipse.jdt.ui/JavadocHoverStyleSheet.css +++ b/org.eclipse.jdt.ui/JavadocHoverStyleSheet.css @@ -12,7 +12,7 @@ h4 { margin-top: 2em; margin-bottom: 0.3em; } h5 { margin-top: 0px; margin-bottom: 0px; } p { margin-top: 1em; margin-bottom: 1em; } pre { margin-left: 0.6em; } -ul { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em;} +ul { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em; } li { margin-top: 0px; margin-bottom: 0px; } li p { margin-top: 0px; margin-bottom: 0px; } ol { margin-top: 0px; margin-bottom: 1em; margin-left: 1em; padding-left: 1em; } @@ -33,6 +33,32 @@ em { font-style: italic; } var { font-style: italic; } th { font-weight: bold; } +/* Styling enhancements */ + +input#formattingSwitch, +input#typeParamsRefsColoringSwitch { display: none; } + +input#formattingSwitch:checked ~ .styleSwitchParent .methodPrependTypeParams { display: block} +input#formattingSwitch:checked ~ .styleSwitchParent .methodParam { display: block; text-indent: 1em; } + +input#formattingSwitch:checked ~ .styleSwitchParent .typeBracketsStart:before, +input#formattingSwitch:checked ~ .styleSwitchParent .methodParams:before { content: " "; } + +input#formattingSwitch:checked ~ .styleSwitchParent .methodQualifier { opacity: 0.6; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodParamName { font-style: italic; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodReturn { font-style: italic; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodName { font-weight: bold; } + +input#formattingSwitch:checked ~ .styleSwitchParent .typeParams { font-weight: 200; } +input#formattingSwitch:checked ~ .styleSwitchParent .typeBrackets { font-weight: bold; } + +/* Start of dynamic type parameters references styling section (do not edit this line) */ +input#typeParamsRefsColoringSwitch:checked ~ .styleSwitchParent .typeParamsReferenceNo-INDEX-, +input#typeParamsRefsColoringSwitch:checked ~ .styleSwitchParent .typeParamsReferenceNo-INDEX- a.header +{ color: -COLOR-; } +/* End of dynamic type parameters references styling section (do not edit this line) */ + + /* Workarounds for new Javadoc stylesheet (1.7) */ ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; diff --git a/org.eclipse.jdt.ui/JavadocViewStyleSheet.css b/org.eclipse.jdt.ui/JavadocViewStyleSheet.css index 7cee7b77c76..cc841e61493 100644 --- a/org.eclipse.jdt.ui/JavadocViewStyleSheet.css +++ b/org.eclipse.jdt.ui/JavadocViewStyleSheet.css @@ -36,6 +36,32 @@ em { font-style: italic; } var { font-style: italic; } th { font-weight: bold; } +/* Styling enhancements */ + +input#formattingSwitch, +input#typeParamsRefsColoringSwitch { display: none; } + +input#formattingSwitch:checked ~ .styleSwitchParent .methodPrependTypeParams { display: block} +input#formattingSwitch:checked ~ .styleSwitchParent .methodParam { display: block; text-indent: 1em; } + +input#formattingSwitch:checked ~ .styleSwitchParent .typeBracketsStart:before, +input#formattingSwitch:checked ~ .styleSwitchParent .methodParams:before { content: " "; } + +input#formattingSwitch:checked ~ .styleSwitchParent .methodQualifier { opacity: 0.6; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodParamName { font-style: italic; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodReturn { font-style: italic; } +input#formattingSwitch:checked ~ .styleSwitchParent .methodName { font-weight: bold; } + +input#formattingSwitch:checked ~ .styleSwitchParent .typeParams { font-weight: 200; } +input#formattingSwitch:checked ~ .styleSwitchParent .typeBrackets { font-weight: bold; } + +/* Start of dynamic type parameters references styling section (do not edit this line) */ +input#typeParamsRefsColoringSwitch:checked ~ .styleSwitchParent .typeParamsReferenceNo-INDEX-, +input#typeParamsRefsColoringSwitch:checked ~ .styleSwitchParent .typeParamsReferenceNo-INDEX- a.header +{ color: -COLOR-; } +/* End of dynamic type parameters references styling section (do not edit this line) */ + + /* Workarounds for new Javadoc stylesheet (1.7) */ ul.blockList li.blockList, ul.blockListLast li.blockList { list-style:none; diff --git a/org.eclipse.jdt.ui/css/e4-dark_jdt_syntaxhighlighting.css b/org.eclipse.jdt.ui/css/e4-dark_jdt_syntaxhighlighting.css index 30b226b7f77..fbd57ccb2c5 100644 --- a/org.eclipse.jdt.ui/css/e4-dark_jdt_syntaxhighlighting.css +++ b/org.eclipse.jdt.ui/css/e4-dark_jdt_syntaxhighlighting.css @@ -97,6 +97,7 @@ IEclipsePreferences#org-eclipse-jdt-ui:org-eclipse-jdt-ui { /* pseudo attribute 'semanticHighlighting.restrictedKeywords.color=204,108,29' 'semanticHighlighting.restrictedKeywords.bold=false' 'sourceHoverBackgroundColor=68,68,68' + 'javadocElementsStyling.darkModeDefaultColors=true' } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java index 9cff65029e0..7c560248730 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaPluginImages.java @@ -59,6 +59,7 @@ public class JavaPluginImages { private static final String T_ELCL= "elcl16"; //$NON-NLS-1$ private static final String T_DLCL= "dlcl16"; //$NON-NLS-1$ private static final String T_ETOOL= "etool16"; //$NON-NLS-1$ + private static final String T_DTOOL= "dtool16"; //$NON-NLS-1$ private static final String T_EVIEW= "eview16"; //$NON-NLS-1$ /* @@ -550,6 +551,9 @@ public class JavaPluginImages { public static final ImageDescriptor DESC_ELCL_WRAP_NOT= createUnManaged(T_ELCL, "wrap_not.png"); //$NON-NLS-1$ public static final ImageDescriptor DESC_DLCL_WRAP_NOT= createUnManaged(T_DLCL, "wrap_not.png"); //$NON-NLS-1$ + public static final ImageDescriptor DESC_ETOOL_JDOC_HOVER_EDIT= createUnManaged(T_ETOOL, "jdoc_hover_edit.png"); //$NON-NLS-1$ + public static final ImageDescriptor DESC_DTOOL_JDOC_HOVER_EDIT= createUnManaged(T_DTOOL, "jdoc_hover_edit.png"); //$NON-NLS-1$ + // Keys for correction proposal. We have to put the image into the registry since "code assist" doesn't // have a life cycle. So no chance to dispose icons. diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java index a6c6b970dc0..31d56f2d487 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2023 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -14,6 +14,7 @@ * Brock Janiczak - [implementation] Streams not being closed in Javadoc views - https://bugs.eclipse.org/bugs/show_bug.cgi?id=214854 * Benjamin Muskalla - [javadoc view] NPE on enumerations - https://bugs.eclipse.org/bugs/show_bug.cgi?id=223586 * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + * Jozef Tomek - add styling enhancements (issue 1073) *******************************************************************************/ package org.eclipse.jdt.internal.ui.infoviews; @@ -50,6 +51,7 @@ import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.jface.action.Separator; +import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.dialogs.MessageDialogWithToggle; import org.eclipse.jface.internal.text.html.BrowserInput; import org.eclipse.jface.internal.text.html.HTMLPrinter; @@ -153,6 +155,8 @@ import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; import org.eclipse.jdt.internal.ui.viewsupport.BindingLinkedLabelComposer; import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; +import org.eclipse.jdt.internal.ui.viewsupport.browser.BrowserTextAccessor; +import org.eclipse.jdt.internal.ui.viewsupport.javadoc.SignatureStylingMenuToolbarAction; /** @@ -419,6 +423,11 @@ protected boolean canEnableFor(IStructuredSelection selection) { */ private ISelectionProvider fInputSelectionProvider; + /** + * Flag to force isIgnoringNewInput() to return false on next execution. + */ + private volatile boolean fIgnoringNewInputOverride; + /** * The Javadoc view's select all action. */ @@ -672,6 +681,26 @@ protected void fillToolBar(IToolBarManager tbm) { tbm.add(fForthAction); tbm.add(new Separator()); + if (fIsUsingBrowserWidget) { + BrowserTextAccessor browserAccessor= new BrowserTextAccessor(fBrowser); + Runnable viewRefreshTask= () -> { + fIgnoringNewInputOverride= true; + setLinkingEnabled(isLinkingEnabled()); // triggers refresh of the view using last set selection + }; + // toolbar widget is being re-created later so we need to do our setup then + var stylingMenuAction= new SignatureStylingMenuToolbarAction(fBrowser.getParent().getShell(), browserAccessor, () -> fOriginalInput, viewRefreshTask) { + // we take advantage of this method being called after toolbar item creation (in ActionContributionItem.fill()) which happens when whole toolbar is being re-created to be displayed + @Override + public void addPropertyChangeListener(IPropertyChangeListener listener) { + super.addPropertyChangeListener(listener); + setup(((ToolBarManager) tbm).getControl()); + } + }; + stylingMenuAction.setId("JavadocView.SignatureStylingMenuToolbarAction"); //$NON-NLS-1$ + tbm.add(stylingMenuAction); + tbm.add(new Separator()); + } + super.fillToolBar(tbm); tbm.add(fOpenBrowserAction); } @@ -1065,7 +1094,7 @@ private String getJavadocHtml(IJavaElement[] result, IWorkbenchPart activePart, if (buffer.length() == 0) return null; - HTMLPrinter.insertPageProlog(buffer, 0, fForegroundColorRGB, fBackgroundColorRGB, fgStyleSheet); + HTMLPrinter.insertPageProlog(buffer, 0, fForegroundColorRGB, fBackgroundColorRGB, JavaElementLinks.modifyCssStyleSheet(fgStyleSheet, buffer)); if (base != null) { int endHeadIdx= buffer.indexOf(""); //$NON-NLS-1$ buffer.insert(endHeadIdx, "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$ @@ -1093,9 +1122,9 @@ private String getInfoText(IJavaElement member, String constantValue, String def // setting haveSource to false lets the JavadocView *always* show qualified type names, // would need to track the source of our input to distinguish classfile/compilationUnit: boolean haveSource= false; - new BindingLinkedLabelComposer(member, label, haveSource).appendBindingLabel(binding, flags); + new BindingLinkedLabelComposer(member, label, haveSource, true).appendBindingLabel(binding, flags); } else { - label= new StringBuffer(JavaElementLinks.getElementLabel(member, flags)); + label= new StringBuffer(JavaElementLinks.getElementLabel(member, flags, false, true)); } if (member.getElementType() == IJavaElement.FIELD && constantValue != null) { label.append(constantValue); @@ -1111,6 +1140,10 @@ protected boolean isIgnoringNewInput(IJavaElement je, IWorkbenchPart part, ISele if (fCurrent != null && fCurrent.getInputElement() instanceof URL) return false; + if (fIgnoringNewInputOverride) { + fIgnoringNewInputOverride= false; + return false; + } if (super.isIgnoringNewInput(je, part, selection) && part instanceof ITextEditor editor && selection instanceof ITextSelection textSel) { diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java index 75c5d6b1368..52f8f787ed9 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -12,6 +12,7 @@ * IBM Corporation - initial API and implementation * Genady Beryozkin - [hovering] tooltip for constant string does not show constant value - https://bugs.eclipse.org/bugs/show_bug.cgi?id=85382 * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + * Jozef Tomek - add styling enhancements (issue 1073) *******************************************************************************/ package org.eclipse.jdt.internal.ui.text.java.hover; @@ -31,6 +32,8 @@ import org.eclipse.swt.graphics.Drawable; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -131,6 +134,8 @@ import org.eclipse.jdt.internal.ui.text.javadoc.JavadocContentAccess2; import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLabelComposer; import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; +import org.eclipse.jdt.internal.ui.viewsupport.browser.BrowserTextAccessor; +import org.eclipse.jdt.internal.ui.viewsupport.javadoc.SignatureStylingMenuToolbarAction; /** @@ -358,6 +363,32 @@ public IInformationControl doCreateInformationControl(Shell parent) { tbm.add(openAttachedJavadocAction); } + var toolbarComposite= tbm.getControl().getParent(); + GridLayout layout= new GridLayout(4, false); + layout.marginHeight= 0; + layout.marginWidth= 0; + layout.horizontalSpacing= 0; + layout.verticalSpacing= 0; + toolbarComposite.setLayout(layout); + + Runnable viewRefreshTask= () -> { + if (iControl.getInput() instanceof JavadocHoverInformationControlInput input) { + iControl.setInput(input.recreateInput()); + } else { + iControl.setVisible(false); + } + }; + ToolBarManager tbmSecondary= new ToolBarManager(SWT.FLAT); + tbmSecondary.createControl(toolbarComposite).setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false)); + BrowserTextAccessor browserAccessor= new BrowserTextAccessor(iControl); + var stylingMenuAction= new SignatureStylingMenuToolbarAction(parent, browserAccessor, + () -> iControl.getInput() == null ? null : iControl.getInput().getHtml(), + viewRefreshTask); + tbmSecondary.add(stylingMenuAction); + tbmSecondary.update(true); + stylingMenuAction.setup(tbmSecondary.getControl()); + tbmSecondary.getControl().moveAbove(toolbarComposite.getChildren()[2]); // move to be before resizeCanvas + IInputChangedListener inputChangeListener= newInput -> { backAction.update(); forwardAction.update(); @@ -494,6 +525,21 @@ public boolean canReuse(IInformationControl control) { } } + private static class JavadocHoverInformationControlInput extends JavadocBrowserInformationControlInput { + private final ITypeRoot fEditorInputElement; + private final IRegion fHoverRegion; + + public JavadocHoverInformationControlInput(JavadocBrowserInformationControlInput previous, IJavaElement element, String html, int leadingImageWidth, ITypeRoot editorInputElement, IRegion hoverRegion) { + super(previous, element, html, leadingImageWidth); + fEditorInputElement= editorInputElement; + fHoverRegion= hoverRegion; + } + + public JavadocBrowserInformationControlInput recreateInput() { + return JavadocHover.getHoverInfo(new IJavaElement[] { getElement() }, fEditorInputElement, fHoverRegion, (JavadocBrowserInformationControlInput) getPrevious()); + } + } + private static final long LABEL_FLAGS= JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_ANNOTATIONS | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS | JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.M_PRE_TYPE_PARAMETERS | JavaElementLabels.T_TYPE_PARAMETERS @@ -785,13 +831,13 @@ public static JavadocBrowserInformationControlInput getHoverInfo(IJavaElement[] RGB fgRGB = registry.getRGB("org.eclipse.jdt.ui.Javadoc.foregroundColor"); //$NON-NLS-1$ RGB bgRGB= registry.getRGB("org.eclipse.jdt.ui.Javadoc.backgroundColor"); //$NON-NLS-1$ - HTMLPrinter.insertPageProlog(buffer, 0, fgRGB, bgRGB, JavadocHover.getStyleSheet()); + HTMLPrinter.insertPageProlog(buffer, 0, fgRGB, bgRGB, JavadocHover.getStyleSheet(buffer)); if (base != null) { int endHeadIdx= buffer.indexOf(""); //$NON-NLS-1$ buffer.insert(endHeadIdx, "\n\n"); //$NON-NLS-1$ //$NON-NLS-2$ } HTMLPrinter.addPageEpilog(buffer); - return new JavadocBrowserInformationControlInput(previousInput, element, buffer.toString(), leadingImageWidth); + return new JavadocHoverInformationControlInput(previousInput, element, buffer.toString(), leadingImageWidth, editorInputElement, hoverRegion); } return null; @@ -806,9 +852,9 @@ private static String getInfoText(IJavaElement element, ITypeRoot editorInputEle StringBuilder label; if (binding != null) { - label= new StringBuilder(JavaElementLinks.getBindingLabel(binding, element, flags, haveSource)); + label= new StringBuilder(JavaElementLinks.getBindingLabel(binding, element, flags, haveSource, true)); } else { - label= new StringBuilder(JavaElementLinks.getElementLabel(element, flags)); + label= new StringBuilder(JavaElementLinks.getElementLabel(element, flags, false, true)); } if (element.getElementType() == IJavaElement.FIELD) { @@ -1027,10 +1073,12 @@ private static String formatWithHexValue(Object constantValue, String hexValue) /** * Returns the Javadoc hover style sheet with the current Javadoc font from the preferences. + * + * @param content html content which will use the style sheet * @return the updated style sheet * @since 3.4 */ - private static String getStyleSheet() { + private static String getStyleSheet(StringBuilder content) { if (fgStyleSheet == null) { fgStyleSheet= loadStyleSheet("/JavadocHoverStyleSheet.css"); //$NON-NLS-1$ } @@ -1039,6 +1087,7 @@ private static String getStyleSheet() { FontData fontData= JFaceResources.getFontRegistry().getFontData(PreferenceConstants.APPEARANCE_JAVADOC_FONT)[0]; css= HTMLPrinter.convertTopLevelFont(css, fontData); } + css= JavaElementLinks.modifyCssStyleSheet(css, content); return css; } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/ArmListeningMenuItemsConfigurer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/ArmListeningMenuItemsConfigurer.java new file mode 100644 index 00000000000..bbd4b69ef98 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/ArmListeningMenuItemsConfigurer.java @@ -0,0 +1,123 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ArmEvent; +import org.eclipse.swt.events.ArmListener; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; + +import org.eclipse.jface.action.ActionContributionItem; + +/** + * Facility to make it possible for menu widget items to react to their arm events, i.e. when menu items + * are chosen (before confirming their selection) by mouse hover or keyboard navigation. + * Menu items become receivers of these events by means of being created from {@link ActionContributionItem} + * that wraps and action implementing convenient callback interface {@link IArmListeningMenuItemAction}. + */ +public final class ArmListeningMenuItemsConfigurer { + + final Runnable postHandleEventAction; + MenuItem lastEnteredItem= null; + IArmListeningMenuItemAction lastEnteredItemAction= null; + + /** + * Does necessary setup of the menu widget to enable forwarding menu items armed events to all of it's + * child menu items that are {@link IArmListeningMenuItemAction receivers} of these events. + * Context specific post-handle action(s) callback postHandleEventAction is executed after + * dispatching menu items armed event to all receiver menu items if at least one of them signals that + * post-handle action(s) should be performed. + *

+ * Attention: This method must be called when all menu item widgets were already created inside the menu. + * @param menu widget containing events receiving items + * @param postHandleEventAction context specific post-handle action(s) callback + */ + public static void registerForMenu(Menu menu, Runnable postHandleEventAction) { + var instance= new ArmListeningMenuItemsConfigurer(postHandleEventAction); + ArmListener listener= instance::handleEvent; + for (MenuItem item : menu.getItems()) { + item.addArmListener(listener); + } + menu.addListener(SWT.Hide, instance::menuHidden); + } + + private ArmListeningMenuItemsConfigurer(Runnable postHandleEventAction) { + this.postHandleEventAction= postHandleEventAction; + } + + private void handleEvent(ArmEvent event) { + var menuItem= (MenuItem) event.widget; + if (lastEnteredItem == menuItem) { + return; + } + boolean runPostAction= false; + if (lastEnteredItemAction != null) { + runPostAction |= lastEnteredItemAction.itemUnarmed(event); + lastEnteredItemAction= null; + } + lastEnteredItem= menuItem; + if (menuItem.getData() instanceof ActionContributionItem actionItem + && actionItem.getAction() instanceof IArmListeningMenuItemAction armedItem) { + lastEnteredItemAction= armedItem; + runPostAction |= armedItem.itemArmed(event); + } + if (postHandleEventAction != null && runPostAction) { + postHandleEventAction.run(); + } + } + + @SuppressWarnings("unused") + private void menuHidden(Event e) { + boolean runPostAction= false; + if (lastEnteredItemAction != null) { + runPostAction= lastEnteredItemAction.itemUnarmed(null); + lastEnteredItemAction= null; + } + if (postHandleEventAction != null && runPostAction) { + postHandleEventAction.run(); + } + } + + /** + * Arm listener tagging interface for menu item actions. If a menu item was created from {@link ActionContributionItem} + * and the action it is wrapping implements this interface, then action's methods defined in this interface are called + * on armed events of the corresponding menu item. + *

+ * Methods return boolean to indicate whether some further post-handle action(s), that is specific to usage context, + * should be performed. + */ + public interface IArmListeningMenuItemAction { + + /** + * Notification when menu item wrapping an action that is target of this call was armed (by mouse hover or keyboard navigation). + * @param event event forwarded from menu's {@link ArmListener} + * @return true if context specific post-handle action(s) should be performed, false otherwise (default) + */ + default boolean itemArmed(ArmEvent event) { + return false; + } + + /** + * Notification when menu item wrapping an action that is target of this call was armed (by mouse hover or keyboard navigation) + * previously but is not anymore. + * @param event event forwarded from menu's {@link ArmListener} + * @return true if context specific post-handle action(s) should be performed, false otherwise (default) + */ + default boolean itemUnarmed(ArmEvent event) { + return false; + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java index 62c53545739..97c1b22ccdd 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/BindingLinkedLabelComposer.java @@ -63,7 +63,11 @@ public class BindingLinkedLabelComposer extends JavaElementLinkedLabelComposer { private boolean fIsFromSource; public BindingLinkedLabelComposer(IJavaElement enclosingElement, StringBuffer buffer, boolean isFromSource) { - super(enclosingElement, buffer); + this(enclosingElement, buffer, isFromSource, false); + } + + public BindingLinkedLabelComposer(IJavaElement enclosingElement, StringBuffer buffer, boolean isFromSource, boolean useEnhancements) { + super(enclosingElement, buffer, useEnhancements); fEnclosingElement= enclosingElement; fIsFromSource= isFromSource; } @@ -437,14 +441,14 @@ else if (getFlag(flags, JavaElementLabels.T_CONTAINER_QUALIFIED)) } } else if (typeBinding.isParameterizedType()) { fBuffer.append(getTypeLink(typeBinding.getTypeDeclaration(), flags)); - fBuffer.append(getLT()); + appendLT(); ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); for (int i= 0; i < typeArguments.length; i++) { if (i > 0) fBuffer.append(JavaElementLabels.COMMA_STRING); appendTypeBindingLabel(typeArguments[i], typeRefFlags); } - fBuffer.append(getGT()); + appendGT(); } else if (typeBinding.isTypeVariable()) { appendNameLink(typeBinding, typeBinding); if (getFlag(flags, TP_BOUNDS)) { @@ -506,13 +510,13 @@ private void appendTypeArgumentsBindingLabel(ITypeBinding[] parameters, String s if (parameters.length > 0) { if (separator != null) fBuffer.append(separator); - fBuffer.append(getLT()); + appendLT(); for (int i = 0; i < parameters.length; i++) { if (i > 0) fBuffer.append(JavaElementLabels.COMMA_STRING); appendTypeBindingLabel(parameters[i], flags); } - fBuffer.append(getGT()); + appendGT(); } } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java index c0c2273a37d..59780fc5cfa 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2022 IBM Corporation and others. + * Copyright (c) 2008, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -11,6 +11,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - Contribution for Bug 403917 - [1.8] Render TYPE_USE annotations in Javadoc hover/view + * Jozef Tomek - add styling enhancements (issue 1073) *******************************************************************************/ package org.eclipse.jdt.internal.ui.viewsupport; @@ -18,18 +19,37 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.function.BiFunction; import org.eclipse.swt.browser.Browser; import org.eclipse.swt.browser.LocationAdapter; import org.eclipse.swt.browser.LocationEvent; import org.eclipse.swt.browser.LocationListener; +import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.widgets.Display; +import org.eclipse.core.runtime.ListenerList; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; + +import org.eclipse.jdt.core.BindingKey; import org.eclipse.jdt.core.IAnnotation; import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IPackageDeclaration; import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.core.ITypeParameter; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.dom.IBinding; @@ -38,6 +58,7 @@ import org.eclipse.jdt.internal.corext.util.Messages; import org.eclipse.jdt.ui.JavaElementLabels; +import org.eclipse.jdt.ui.PreferenceConstants; import org.eclipse.jdt.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.JavaUIMessages; @@ -50,6 +71,41 @@ */ public class JavaElementLinks { + /** + * ID of the checkbox in generated HTML content that toggles formatting inside element labels. + */ + public static final String CHECKBOX_ID_FORMATTIG= "formattingSwitch"; //$NON-NLS-1$ + + private static final String PREFERENCE_KEY_POSTFIX_TYPE_PARAMETERS_REFERENCES_COLORING= "javadocElementsStyling.typeParamsReferencesColoring"; //$NON-NLS-1$ + + private static final String PREFERENCE_KEY_ENABLED= "javadocElementsStyling.enabled"; //$NON-NLS-1$ + private static final String PREFERENCE_KEY_DARK_MODE_DEFAULT_COLORS= "javadocElementsStyling.darkModeDefaultColors"; //$NON-NLS-1$ + // uses 1-based indexing + private static final String PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR= "javadocElementsStyling.typesParamsReferenceColor_"; //$NON-NLS-1$ + /** + * Maximum number of type parameters references for which we support setting custom color + */ + private static final int MAX_COLOR_INDEX= 16; + + + private static final String CSS_CLASS_SWITCH_PARENT= "styleSwitchParent"; //$NON-NLS-1$ + // both use 1-based indexing + private static final String CSS_CLASS_TYPE_PARAMETERS_REFERENCE_PREFIX= "typeParamsReference typeParamsReferenceNo"; //$NON-NLS-1$ + + private static final String CSS_SECTION_START_TYPE_PARAMETERS_REFERENCES= "/* Start of dynamic type parameters references styling section (do not edit this line) */"; //$NON-NLS-1$ + private static final String CSS_SECTION_END_TYPE_PARAMETERS_REFERENCES= "/* End of dynamic type parameters references styling section (do not edit this line) */"; //$NON-NLS-1$ + private static final String CSS_PLACEHOLDER_INDEX= "-INDEX-"; //$NON-NLS-1$ + private static final String CSS_PLACEHOLDER_COLOR= "-COLOR-"; //$NON-NLS-1$ + + + private static String[] CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES= new String[4]; + private static final ReentrantLock CSS_FRAGMENTS_CACHE_LOCK= new ReentrantLock(); + private static final IPropertyChangeListener ENHANCEMENTS_PROPERTIES_CHANGE_LISTENER= JavaElementLinks::enhancementsSettingsChangeListener; + private static final IPropertyChangeListener COLOR_PROPERTIES_CHANGE_LISTENER= JavaElementLinks::cssFragmentsCacheResetListener; + private static final ListenerList CONFIG_LISTENERS = new ListenerList<>(); + +// private static boolean + /** * A handler is asked to handle links to targets. * @@ -96,14 +152,38 @@ public interface ILinkHandler { static class JavaElementLinkedLabelComposer extends JavaElementLabelComposer { private final IJavaElement fElement; + private final boolean noEnhancements; + private final boolean enableFormatting; + private final boolean enableTypeParamsColoring; + + private boolean appendHoverParent= true; + private int nextNestingLevel= 1; + private Map typesIds= new TreeMap<>(); + private int nextTypeNo= 1; + private int nextParamNo= 1; + private boolean appendingMethodQualification= false; + private boolean typeStyleClassApplied= false; + private boolean inBoundedTypeParam= false; public JavaElementLinkedLabelComposer(IJavaElement member, StringBuffer buf) { + this(member, buf, false); + } + + public JavaElementLinkedLabelComposer(IJavaElement member, StringBuffer buf, boolean useEnhancements) { super(buf); if (member instanceof IPackageDeclaration) { fElement= member.getAncestor(IJavaElement.PACKAGE_FRAGMENT); } else { fElement= member; } + if (getStylingEnabledPreference() && useEnhancements) { + noEnhancements= false; + enableFormatting= true; + enableTypeParamsColoring= getPreferenceForTypeParamsColoring(); + } else { + noEnhancements= true; + enableFormatting= enableTypeParamsColoring= false; + } } @Override @@ -153,13 +233,26 @@ private String getPackageFragmentElementName(IJavaElement javaElement) { } @Override - protected String getGT() { - return ">"; //$NON-NLS-1$ + protected void appendGT() { + if (noEnhancements) { + fBuffer.append(">"); //$NON-NLS-1$ + } else { + fBuffer.append(""); // close 'typeParams' span //$NON-NLS-1$ + fBuffer.append(">"); //$NON-NLS-1$ + nextNestingLevel--; + } + } @Override - protected String getLT() { - return "<"; //$NON-NLS-1$ + protected void appendLT() { + if (noEnhancements) { + fBuffer.append("<"); //$NON-NLS-1$ + } else { + fBuffer.append("<"); //$NON-NLS-1$ + fBuffer.append(""); //$NON-NLS-1$ + nextNestingLevel++; + } } @Override @@ -178,13 +271,39 @@ protected String getSimpleTypeName(IJavaElement enclosingElement, String typeSig } } + String retVal= typeName; try { String uri= createURI(JAVADOC_SCHEME, enclosingElement, qualifiedName, null, null); - return createHeaderLink(uri, typeName, title); + retVal= createHeaderLink(uri, typeName, title); } catch (URISyntaxException e) { JavaPlugin.log(e); - return typeName; } + + if (!noEnhancements && !inBoundedTypeParam) { + if ((Signature.getTypeSignatureKind(typeSig) == Signature.TYPE_VARIABLE_SIGNATURE && !typeStyleClassApplied) + || (Signature.getTypeSignatureKind(typeSig) == Signature.CLASS_TYPE_SIGNATURE && nextNestingLevel > 1)) { + return wrapWithTypeClass(typeName, retVal); + } else { + return retVal; + } + } else { + return retVal; + } + } + + private String wrapWithTypeClass(String typeName, String value) { + return "" //$NON-NLS-1$ + + value + + ""; //$NON-NLS-1$ + } + + private String getTypeStylingClass(String typeName) { + Integer typeId; + if ((typeId= typesIds.putIfAbsent(typeName, nextTypeNo)) == null) { + typeId= nextTypeNo++; + } + return CSS_CLASS_TYPE_PARAMETERS_REFERENCE_PREFIX + typeId; } @Override @@ -204,12 +323,251 @@ protected void appendAnnotationLabels(IAnnotation[] annotations, long flags) thr super.appendAnnotationLabels(annotations, flags); fBuffer.append(""); //$NON-NLS-1$ } + + @Override + public void appendElementLabel(IJavaElement element, long flags) { + if (noEnhancements) { + super.appendElementLabel(element, flags); + return; + } + if (appendingMethodQualification) { + // method label contains nested method label (eg. lambdas), we need to end method qualification if started + fBuffer.append(""); //$NON-NLS-1$ + appendingMethodQualification= false; + } + if (appendHoverParent) { + appendHoverParent= false; + + // formatting checkbox + fBuffer.append(""); //$NON-NLS-1$ + + // typeParametersColoring checkbox + fBuffer.append(""); //$NON-NLS-1$ + + // encompassing for everything styled based on checkboxes checked state + fBuffer.append(""); //$NON-NLS-1$ //$NON-NLS-2$ + + // actual signature content + super.appendElementLabel(element, flags); + + fBuffer.append(""); //$NON-NLS-1$ + appendHoverParent= true; + } else { + super.appendElementLabel(element, flags); + } + } + + @Override + protected void appendMethodPrependedTypeParams(IMethod method, long flags, BindingKey resolvedKey, String resolvedSignature) throws JavaModelException { + if (noEnhancements) { + super.appendMethodPrependedTypeParams(method, flags, resolvedKey, resolvedSignature); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodPrependedTypeParams(method, flags, resolvedKey, resolvedSignature); + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendMethodPrependedReturnType(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + if (noEnhancements) { + super.appendMethodPrependedReturnType(method, flags, resolvedSignature); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodPrependedReturnType(method, flags, resolvedSignature); + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendMethodQualification(IMethod method, long flags) { + if (noEnhancements) { + super.appendMethodQualification(method, flags); + } else { + appendingMethodQualification= true; + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodQualification(method, flags); + if (appendingMethodQualification) { + fBuffer.append(""); //$NON-NLS-1$ + appendingMethodQualification= false; + } + } + } + + @Override + protected void appendMethodName(IMethod method) { + if (noEnhancements) { + super.appendMethodName(method); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodName(method); + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendMethodParams(IMethod method, long flags, String resolvedSignature) throws JavaModelException { + if (noEnhancements) { + super.appendMethodParams(method, flags, resolvedSignature); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodParams(method, flags, resolvedSignature); + fBuffer.append(""); //$NON-NLS-1$ + nextParamNo= 1; + } + } + + @Override + protected void appendMethodParam(IMethod method, long flags, IAnnotation[] annotations, String paramSignature, String name, boolean renderVarargs, boolean isLast) throws JavaModelException { + if (noEnhancements) { + super.appendMethodParam(method, flags, annotations, paramSignature, name, renderVarargs, isLast); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodParam(method, flags, annotations, paramSignature, name, renderVarargs, isLast); + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendMethodParamName(String name) { + if (noEnhancements) { + super.appendMethodParamName(name); + } else { + fBuffer.append(""); //$NON-NLS-1$ + super.appendMethodParamName(name); + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendTypeParameterWithBounds(ITypeParameter typeParameter, long flags) throws JavaModelException { + if (noEnhancements) { + super.appendTypeParameterWithBounds(typeParameter, flags); + } else { + fBuffer.append(""); //$NON-NLS-1$ + inBoundedTypeParam= true; + super.appendTypeParameterWithBounds(typeParameter, flags); + inBoundedTypeParam= false; + fBuffer.append(""); //$NON-NLS-1$ + } + } + + @Override + protected void appendWildcardTypeSignature(String prefix, IJavaElement enclosingElement, String typeSignature, long flags) { + if (noEnhancements) { + super.appendWildcardTypeSignature(prefix, enclosingElement, typeSignature, flags); + } else { + int sigKind= Signature.getTypeSignatureKind(typeSignature); + if (sigKind == Signature.TYPE_VARIABLE_SIGNATURE || sigKind == Signature.CLASS_TYPE_SIGNATURE) { + typeStyleClassApplied= true; + String typeName= super.getSimpleTypeName(enclosingElement, typeSignature); + fBuffer.append(""); //$NON-NLS-1$ + } + super.appendWildcardTypeSignature(prefix, enclosingElement, typeSignature, flags); + if (typeStyleClassApplied) { + fBuffer.append(""); //$NON-NLS-1$ + typeStyleClassApplied= false; + } + } + } + + @Override + protected void appendTypeArgumentSignaturesLabel(IJavaElement enclosingElement, String[] typeArgsSig, long flags) { + if (inBoundedTypeParam) { + inBoundedTypeParam= false; + super.appendTypeArgumentSignaturesLabel(enclosingElement, typeArgsSig, flags); + inBoundedTypeParam= true; + } else { + super.appendTypeArgumentSignaturesLabel(enclosingElement, typeArgsSig, flags); + } + } } public static final String OPEN_LINK_SCHEME= CoreJavaElementLinks.OPEN_LINK_SCHEME; public static final String JAVADOC_SCHEME= CoreJavaElementLinks.JAVADOC_SCHEME; public static final String JAVADOC_VIEW_SCHEME= CoreJavaElementLinks.JAVADOC_VIEW_SCHEME; + public static void initDefaultPreferences(IPreferenceStore store) { + initDefaultColors(store); + store.setDefault(PREFERENCE_KEY_ENABLED, true); + store.setDefault(PREFERENCE_KEY_POSTFIX_TYPE_PARAMETERS_REFERENCES_COLORING, true); + store.addPropertyChangeListener(COLOR_PROPERTIES_CHANGE_LISTENER); + store.addPropertyChangeListener(ENHANCEMENTS_PROPERTIES_CHANGE_LISTENER); + } + + public static void initDefaultColors(IPreferenceStore store) { + if (store.getBoolean(PREFERENCE_KEY_DARK_MODE_DEFAULT_COLORS)) { + var color= new RGB(177, 102, 218); // semanticHighlighting.typeArgument.color in css\e4-dark_jdt_syntaxhighlighting.css + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 1), color); + + color= new RGB(255, 140, 0); // CSS 'DarkOrange' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 2), color); + + color= new RGB(144, 238, 144); // CSS 'LightGreen' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 3), color); + + color= new RGB(0, 191, 255); // CSS 'DeepSkyBlue' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 4), color); + } else { + // slightly brighter than SemanticHighlightings.TypeArgumentHighlighting's default color to work better on yellow-ish background + var color= new RGB(60, 179, 113); // CSS 'MediumSeaGreen' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 1), color); + + color= new RGB(255, 140, 0); // CSS 'DarkOrange' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 2), color); + + color= new RGB(153, 50, 204); // CSS 'DarkOrchid' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 3), color); + + color= new RGB(65, 105, 225); // CSS 'RoyalBlue' + PreferenceConverter.setDefault(store, getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 4), color); + } + } + + private static void enhancementsSettingsChangeListener(PropertyChangeEvent event) { + if (PREFERENCE_KEY_DARK_MODE_DEFAULT_COLORS.equals(event.getProperty())) { + // taking advantage of PREFERENCE_KEY_DARK_MODE_DEFAULT_COLORS change instead of more complicated OSGi event listener + initDefaultColors(preferenceStore()); + cssFragmentsCacheResetListener(null); + } else if (PREFERENCE_KEY_ENABLED.equals(event.getProperty())) { + CONFIG_LISTENERS.forEach(l -> l.stylingStateChanged((Boolean) event.getNewValue())); + } else if (event.getProperty().startsWith(PREFERENCE_KEY_POSTFIX_TYPE_PARAMETERS_REFERENCES_COLORING)) { + CONFIG_LISTENERS.forEach(l -> l.parametersColoringStateChanged((Boolean) event.getNewValue())); + } else if (event.getProperty().startsWith(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR)) { + CONFIG_LISTENERS.forEach(IStylingConfigurationListener::parametersColorChanged); + } + } + + private static void cssFragmentsCacheResetListener(PropertyChangeEvent event) { + if (event == null || event.getProperty().startsWith(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR)) { + try { + if (CSS_FRAGMENTS_CACHE_LOCK.tryLock(500, TimeUnit.MILLISECONDS)) { + try { + CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES= new String[4]; + } finally { + CSS_FRAGMENTS_CACHE_LOCK.unlock(); + } + } + } catch (InterruptedException e1) { + JavaPlugin.logErrorMessage("Interrupted while waiting for CSS fragments cache lock, cache reset unsuccessful"); //$NON-NLS-1$ + } + } + } + private JavaElementLinks() { // static only } @@ -400,10 +758,26 @@ public static String getElementLabel(IJavaElement element, long flags) { * @since 3.6 */ public static String getElementLabel(IJavaElement element, long flags, boolean linkAllNames) { + return getElementLabel(element, flags, linkAllNames, false); + } + + /** + * Returns the label for a Java element with the flags as defined by {@link JavaElementLabels}. + * Referenced element names in the label are rendered as header links. + * If linkAllNames is false, don't link the name of the given element + * + * @param element the element to render + * @param flags the rendering flags + * @param linkAllNames if true, link all names; if false, link all names except original element's name + * null means no enhanced styling + * @return the label of the Java element + * @since 3.6 + */ + public static String getElementLabel(IJavaElement element, long flags, boolean linkAllNames, boolean useEnhancements) { StringBuffer buf= new StringBuffer(); if (!Strings.USE_TEXT_PROCESSOR) { - new JavaElementLinkedLabelComposer(linkAllNames ? null : element, buf).appendElementLabel(element, flags); + new JavaElementLinkedLabelComposer(linkAllNames ? null : element, buf, useEnhancements).appendElementLabel(element, flags); return Strings.markJavaElementLabelLTR(buf.toString()); } else { String label= JavaElementLabels.getElementLabel(element, flags); @@ -423,14 +797,210 @@ public static String getElementLabel(IJavaElement element, long flags, boolean l * @since 3.11 */ public static String getBindingLabel(IBinding binding, IJavaElement element, long flags, boolean haveSource) { + return getBindingLabel(binding, element, flags, haveSource, false); + } + + /** + * Returns the label for a binding with the flags as defined by {@link JavaElementLabels}. + * Referenced element names in the label are rendered as header links. + * + * @param binding the binding to render + * @param element the corresponding Java element, used for javadoc hyperlinks + * @param flags the rendering flags + * @param haveSource true when looking at an ICompilationUnit which enables the use of short type names + * @param useEnhancements whether to use enhanced styling of HTML content for element labels + * @return the label of the binding + * @since 3.11 + */ + public static String getBindingLabel(IBinding binding, IJavaElement element, long flags, boolean haveSource, boolean useEnhancements) { StringBuffer buf= new StringBuffer(); if (!Strings.USE_TEXT_PROCESSOR) { - new BindingLinkedLabelComposer(element, buf, haveSource).appendBindingLabel(binding, flags); + new BindingLinkedLabelComposer(element, buf, haveSource, useEnhancements).appendBindingLabel(binding, flags); return Strings.markJavaElementLabelLTR(buf.toString()); } else { String label= JavaElementLabels.getElementLabel(element, flags); return label.replace("<", "<").replace(">", ">"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } } + + public static boolean getStylingEnabledPreference() { + return preferenceStore().getBoolean(PREFERENCE_KEY_ENABLED); + } + + public static boolean getPreferenceForTypeParamsColoring() { + return preferenceStore().getBoolean(PREFERENCE_KEY_POSTFIX_TYPE_PARAMETERS_REFERENCES_COLORING); + } + + public static void setStylingEnabledPreference(boolean value) { + preferenceStore().setValue(PREFERENCE_KEY_ENABLED, value); + } + + public static void setPreferenceForTypeParamsColoring(boolean value) { + preferenceStore().setValue(PREFERENCE_KEY_POSTFIX_TYPE_PARAMETERS_REFERENCES_COLORING, value); + } + + public static RGB getColorPreferenceForTypeParamsReference(int referenceIndex) { + var color= PreferenceConverter.getColor(preferenceStore(), getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, referenceIndex)); + if (PreferenceConverter.COLOR_DEFAULT_DEFAULT == color) { + // for unconfigured color indexes alternate between first 4 colors + return PreferenceConverter.getColor(preferenceStore(), getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, 1 + ((referenceIndex + 3) % 4))); + } + return color; + } + + public static void setColorPreferenceForTypeParamsReference(int referenceIndex, RGB color) { + PreferenceConverter.setValue(preferenceStore(), getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, referenceIndex), color); + } + + public static String modifyCssStyleSheet(String css, StringBuilder buffer) { + int startPos= buffer.indexOf(CSS_CLASS_SWITCH_PARENT); + if (startPos < 0) { + return css; + } + StringBuilder cssContent= new StringBuilder(); + + int maxTypeParamNo= getMaxIndexOfStyle(buffer, StringBuilder::indexOf, CSS_CLASS_TYPE_PARAMETERS_REFERENCE_PREFIX); + + var locked= false; + try { + locked= CSS_FRAGMENTS_CACHE_LOCK.tryLock(100, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + JavaPlugin.logErrorMessage("Interrupted while waiting for CSS fragments cache lock, proceeding without using cache"); //$NON-NLS-1$ + } + try { + if (locked) { + if (CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES.length < maxTypeParamNo) { + CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES= Arrays.copyOf(CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES, maxTypeParamNo); + } + } + var processedUntil= processColoringSection(css, cssContent, maxTypeParamNo, locked); + cssContent.append(css, processedUntil, css.length()); + return cssContent.toString(); + } catch (Exception e) { + JavaPlugin.log(e); + return css; + } finally { + if (locked) { + CSS_FRAGMENTS_CACHE_LOCK.unlock(); + } + } + } + + private static int processColoringSection(String cssTemplate, StringBuilder outputCss, int iterations, boolean fragmentsCacheLock) { + var sectionStart= cssTemplate.indexOf(CSS_SECTION_START_TYPE_PARAMETERS_REFERENCES); + outputCss.append(cssTemplate, 0, sectionStart); + + sectionStart += CSS_SECTION_START_TYPE_PARAMETERS_REFERENCES.length(); + var sectionEnd= cssTemplate.indexOf(CSS_SECTION_END_TYPE_PARAMETERS_REFERENCES, sectionStart); + for (int i= iterations - 1; i >= 0 ; i--) { + if (fragmentsCacheLock && CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES.length > i && CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES[i] != null) { + // re-use cached fragment + outputCss.append(CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES[i]); + } else { + var section= cssTemplate.substring(sectionStart, sectionEnd); + var sectionBuf= new StringBuilder(section); + int pos; + int index = i + 1; // color styles in CSS and preference keys are 1-based + while ((pos= sectionBuf.indexOf(CSS_PLACEHOLDER_INDEX)) != -1) { + sectionBuf.replace(pos, pos + CSS_PLACEHOLDER_INDEX.length(), String.valueOf(index)); + } + pos= sectionBuf.indexOf(CSS_PLACEHOLDER_COLOR); + sectionBuf.replace(pos, pos + CSS_PLACEHOLDER_COLOR.length(), getCssColor(getColorPreferenceForTypeParamsReference(index))); + if (fragmentsCacheLock && CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES.length > i) { // cache fragment if possible + CSS_FRAGMENTS_CACHE_TYPE_PARAMETERS_REFERENCES[i]= sectionBuf.toString(); + } + outputCss.append(sectionBuf); + } + } + return sectionEnd + CSS_SECTION_END_TYPE_PARAMETERS_REFERENCES.length(); + } + + private static String getCssColor(RGB color) { + return "rgb(" + color.red + ", " + color.green + ", " + color.blue + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + + public static int getNumberOfTypeParamsReferences(String content) { + return getMaxIndexOfStyle(content, String::indexOf, CSS_CLASS_TYPE_PARAMETERS_REFERENCE_PREFIX); + } + + private static int getMaxIndexOfStyle(T content, BiFunction indexOfGetter, String stylePrefix) { + int i= 0; + while (indexOfGetter.apply(content, stylePrefix + ++i) != -1) { /* no-op */ } + return i - 1; + } + + public static Integer[] getColorPreferencesIndicesForTypeParamsReference() { + List retVal= new ArrayList<>(MAX_COLOR_INDEX); + for (int i= 1; i <= MAX_COLOR_INDEX; i++) { + if (i <= 4) { + // pretend first 4 colors are always set (since we have defaults for them) + retVal.add(i); + } else { + String key= getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, i); + if (preferenceStore().contains(key)) { + retVal.add(i); + } + } + } + return retVal.toArray(Integer[]::new); + } + + public static void resetAllColorPreferencesToDefaults() { + var store= preferenceStore(); + store.removePropertyChangeListener(COLOR_PROPERTIES_CHANGE_LISTENER); + store.removePropertyChangeListener(ENHANCEMENTS_PROPERTIES_CHANGE_LISTENER); + try { + for (int i= 1; i <= MAX_COLOR_INDEX; i++) { + String key= getColorPreferenceKey(PREFERENCE_KEY_PREFIX_TYPE_PARAMETERS_REFERENCE_COLOR, i); + if (!store.isDefault(key)) { + store.setToDefault(key); + } + } + cssFragmentsCacheResetListener(null); // clear CSS fragments cache + } finally { + store.addPropertyChangeListener(COLOR_PROPERTIES_CHANGE_LISTENER); + store.addPropertyChangeListener(ENHANCEMENTS_PROPERTIES_CHANGE_LISTENER); + CONFIG_LISTENERS.forEach(IStylingConfigurationListener::parametersColorChanged); + } + } + + private static String getColorPreferenceKey(String prefix, int index) { + return prefix + index; + } + + private static IPreferenceStore preferenceStore() { + return PreferenceConstants.getPreferenceStore(); + } + + public static void addStylingConfigurationListener(IStylingConfigurationListener listener) { + CONFIG_LISTENERS.add(listener); + } + + public static void removeStylingConfigurationListener(IStylingConfigurationListener listener) { + CONFIG_LISTENERS.remove(listener); + } + + /** + * Styling configuration listener is notified when Javadoc styling enhancements are switched on or off via preference. + */ + public static interface IStylingConfigurationListener { + /** + * Called when all Javadoc styling enhancements have been toggled. + * @param isEnabled whether all styling enhancements were turned on or off + */ + void stylingStateChanged(boolean isEnabled); + + /** + * Called when parameters coloring styling enhancement for Javadoc have been toggled. + * @param isEnabled whether parameters coloring styling enhancement was turned on or off + */ + void parametersColoringStateChanged(boolean isEnabled); + + /** + * Called when some color used for parameters coloring styling enhancement for Javadoc has been changed. + */ + void parametersColorChanged(); + } + } diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/MenuVisibilityMenuItemsConfigurer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/MenuVisibilityMenuItemsConfigurer.java new file mode 100644 index 00000000000..90cbf7796be --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/MenuVisibilityMenuItemsConfigurer.java @@ -0,0 +1,89 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport; + +import java.util.function.BiConsumer; + +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.events.MenuListener; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; + +import org.eclipse.jface.action.ActionContributionItem; + +/** + * Facility to make it possible for menu widget items to react to their parent menu's visibility events. + * Menu items become receivers of these events by means of being created from {@link ActionContributionItem} + * that wraps and action implementing convenient callback interface {@link IMenuVisibilityMenuItemAction}. + */ +public final class MenuVisibilityMenuItemsConfigurer { + + /** + * Does necessary setup of the menu widget to enable forwarding menu's visibility events to all of it's + * child menu items that are {@link IMenuVisibilityMenuItemAction receivers} of these events. + *

+ * Attention: This method must be called when all menu item widgets were already created inside the menu. + * @param menu widget containing events receiving items + */ + public static void registerForMenu(Menu menu) { + menu.addMenuListener(new MenuListenerImpl(menu)); + } + + private static class MenuListenerImpl implements MenuListener { + final Menu menu; + + private MenuListenerImpl(Menu menu) { + this.menu= menu; + } + + @Override + public void menuShown(MenuEvent e) { + handleEvent(e, IMenuVisibilityMenuItemAction::menuShown); + } + + @Override + public void menuHidden(MenuEvent e) { + handleEvent(e, IMenuVisibilityMenuItemAction::menuHidden); + } + + private void handleEvent(MenuEvent e, BiConsumer callback) { + for (MenuItem item : menu.getItems()) { + if (item.getData() instanceof ActionContributionItem actionItem + && actionItem.getAction() instanceof IMenuVisibilityMenuItemAction listener) { + callback.accept(listener, e); + } + } + } + } + + /** + * Menu visibility listener tagging interface for menu item actions. If a menu item was created from {@link ActionContributionItem} + * and the action it is wrapping implements this interface, then action's methods defined in this interface are called + * on change of menu visibility. + */ + public interface IMenuVisibilityMenuItemAction { + + /** + * Notification when the menu, that contains menu item wrapping an action that is target of this call, was shown. + * @param event event forwarded from menu's {@link MenuListener} + */ + default void menuShown(MenuEvent event) {} + + /** + * Notification when the menu, that contains menu item wrapping an action that is target of this call, was hidden. + * @param event event forwarded from menu's {@link MenuListener} + */ + default void menuHidden(MenuEvent event) {} + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/browser/BrowserTextAccessor.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/browser/BrowserTextAccessor.java new file mode 100644 index 00000000000..6aecb690c41 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/browser/BrowserTextAccessor.java @@ -0,0 +1,84 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport.browser; + +import java.util.function.Supplier; + +import org.eclipse.swt.browser.Browser; +import org.eclipse.swt.browser.LocationEvent; +import org.eclipse.swt.browser.LocationListener; + +import org.eclipse.core.runtime.ListenerList; + +import org.eclipse.jface.internal.text.html.BrowserInformationControl; + +/** + * Provides access to the text of the {@link Browser} internally created & used by {@link BrowserInformationControl}. + */ +public class BrowserTextAccessor { + + private final ListenerList listeners= new ListenerList<>(); + private Browser browser; + + public BrowserTextAccessor(BrowserInformationControl iControl) { + // only way so far to get hold of reference to browser is to get it through LocationListener + iControl.addLocationListener(new BrowserTextAccessorLocationListener()); + } + + public BrowserTextAccessor(Browser browser) { + this.browser= browser; + browser.addLocationListener(new BrowserTextAccessorLocationListener()); + } + + public String getText() { + return browser.getText(); + } + + public void addContentChangedListener(IBrowserContentChangeListener listener) { + listeners.add(listener); + } + + public void removeContentChangedListener(IBrowserContentChangeListener listener) { + listeners.remove(listener); + } + + // to avoid BrowserTextAccessor itself implementing LocationListener + private class BrowserTextAccessorLocationListener implements LocationListener { + + @Override + public void changing(LocationEvent event) { + if (browser == null && event.widget instanceof Browser) { + browser= (Browser) event.widget; + } + } + + @Override + public void changed(LocationEvent event) { + listeners.forEach(listener -> listener.browserContentChanged(BrowserTextAccessor.this::getText)); + } + } + + /** + * Listener for browser content changes. + */ + public interface IBrowserContentChangeListener { + + /** + * Notification when content inside the browser accessed through {@link BrowserTextAccessor} was changed. + * @param contentAccessor supplier of the new browser content + */ + void browserContentChanged(Supplier contentAccessor); + } + +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.java new file mode 100644 index 00000000000..eb27615d85e --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.java @@ -0,0 +1,43 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport.javadoc; + +import org.eclipse.osgi.util.NLS; + +/** + * Helper class to get NLSed messages. + */ +public class JavadocStylingMessages extends NLS { + + private static final String BUNDLE_NAME= JavadocStylingMessages.class.getName(); + + private JavadocStylingMessages() { + // Do not instantiate + } + + public static String JavadocStyling_typeParamsColoring; + public static String JavadocStyling_enabledTooltip; + public static String JavadocStyling_disabledTooltip; + public static String JavadocStyling_noEnhancements; + public static String JavadocStyling_colorPreferences_menu; + public static String JavadocStyling_colorPreferences_typeParameter; + public static String JavadocStyling_colorPreferences_resetAll; + public static String JavadocStyling_colorPreferences_noTypeParameters; + public static String JavadocStyling_colorPreferences_unusedTypeParameter; + + static { + NLS.initializeMessages(BUNDLE_NAME, JavadocStylingMessages.class); + } + +} diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.properties new file mode 100644 index 00000000000..d2249e7dbae --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/JavadocStylingMessages.properties @@ -0,0 +1,22 @@ +############################################################################### +# Copyright (c) 2024 Jozef Tomek and others. +# +# This program and the accompanying materials +# are made available under the terms of the Eclipse Public License 2.0 +# which accompanies this distribution, and is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Jozef Tomek - initial API and implementation +############################################################################### +JavadocStyling_typeParamsColoring=Type Parameters Coloring +JavadocStyling_enabledTooltip=Disable styling enhancements +JavadocStyling_disabledTooltip=Enable styling enhancements +JavadocStyling_noEnhancements=No styling enhancements available for JavaDoc +JavadocStyling_colorPreferences_menu=Colors Preferences +JavadocStyling_colorPreferences_typeParameter=Type Parameter #{0} +JavadocStyling_colorPreferences_resetAll=Restore Defaults +JavadocStyling_colorPreferences_noTypeParameters=No Type Parameters In Javadoc +JavadocStyling_colorPreferences_unusedTypeParameter=Color not used In Javadoc \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorPreferenceMenuItem.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorPreferenceMenuItem.java new file mode 100644 index 00000000000..63f7c45ebd9 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorPreferenceMenuItem.java @@ -0,0 +1,85 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport.javadoc; + +import java.util.Objects; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.ImageDataProvider; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.resource.ImageDescriptor; + +import org.eclipse.jdt.internal.corext.util.Messages; + +/** + * Menu item action for particular color index used for coloring aspect of javadoc styling that presents + * current color preference persisted in preference store and allows to change it via native color picker. + */ +class SignatureStylingColorPreferenceMenuItem extends Action implements ImageDataProvider { + private final Shell shell; + private final Integer colorIdx; + private final Function colorPreferenceGetter; + private final BiConsumer colorPreferenceSetter; + + SignatureStylingColorPreferenceMenuItem(Shell shell, String textPrefix, int colorIdx, Function colorPreferenceGetter, BiConsumer colorPreferenceSetter) { + super(Messages.format(textPrefix, colorIdx)); + this.shell= Objects.requireNonNull(shell); + this.colorIdx= colorIdx; + this.colorPreferenceGetter= Objects.requireNonNull(colorPreferenceGetter); + this.colorPreferenceSetter= Objects.requireNonNull(colorPreferenceSetter); + setId(SignatureStylingColorPreferenceMenuItem.class.getSimpleName() + "_" + colorIdx); //$NON-NLS-1$ + setImageDescriptor(ImageDescriptor.createFromImageDataProvider(this)); + } + + @Override + public ImageData getImageData(int zoom) { + Image image= new Image(shell.getDisplay(), 16, 16); + GC gc= new GC(image); + + gc.setForeground(shell.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BORDER)); + gc.setBackground(new Color(shell.getDisplay(), getCurrentColor())); + gc.fillRectangle(image.getBounds()); + gc.setLineWidth(2); + gc.drawRectangle(image.getBounds()); + gc.dispose(); + + ImageData data= image.getImageData(zoom); + image.dispose(); + return data; + } + + private RGB getCurrentColor() { + return colorPreferenceGetter.apply(colorIdx); + } + + @Override + public void run() { + ColorDialog colorDialog= new ColorDialog(shell); + colorDialog.setRGB(getCurrentColor()); + RGB newColor= colorDialog.open(); + if (newColor != null) { + colorPreferenceSetter.accept(colorIdx, newColor); + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorSubMenuItem.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorSubMenuItem.java new file mode 100644 index 00000000000..cb9421de6b5 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingColorSubMenuItem.java @@ -0,0 +1,153 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport.javadoc; + +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IMenuCreator; +import org.eclipse.jface.action.Separator; + +import org.eclipse.jdt.internal.corext.util.Messages; + +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; +import org.eclipse.jdt.internal.ui.viewsupport.MenuVisibilityMenuItemsConfigurer.IMenuVisibilityMenuItemAction; + +/** + * Menu item action for building & presenting color preferences sub-menu of javadoc styling menu. + */ +class SignatureStylingColorSubMenuItem extends Action implements IMenuCreator, IMenuVisibilityMenuItemAction { + private final Shell parentShell; + private final Supplier javadocContentSupplier; + + private Menu menu= null; + + public SignatureStylingColorSubMenuItem(Shell parent, Supplier javadocContentSupplier) { + super(JavadocStylingMessages.JavadocStyling_colorPreferences_menu, AS_DROP_DOWN_MENU); + this.parentShell= Objects.requireNonNull(parent); + this.javadocContentSupplier= Objects.requireNonNull(javadocContentSupplier); + setMenuCreator(this); + } + + @Override + public Menu getMenu(Menu parent) { + var content= javadocContentSupplier.get(); + if (menu == null && content != null) { + menu= new Menu(parent); + + new ActionContributionItem(new ResetSignatureStylingColorsPreferencesMenuItem()).fill(menu, -1); + new Separator().fill(menu, -1); + + int typeParamsReferencesCount= JavaElementLinks.getNumberOfTypeParamsReferences(content); + for (int i= 1; i <= typeParamsReferencesCount; i++) { + var item= new ActionContributionItem(new SignatureStylingColorPreferenceMenuItem( + parentShell, + JavadocStylingMessages.JavadocStyling_colorPreferences_typeParameter, + i, + JavaElementLinks::getColorPreferenceForTypeParamsReference, + JavaElementLinks::setColorPreferenceForTypeParamsReference)); + item.fill(menu, -1); + } + if (typeParamsReferencesCount == 0) { + new ActionContributionItem(new NoSignatureStylingTypeParametersMenuItem()).fill(menu, -1); + } + + var typeParamsReferenceIndices= JavaElementLinks.getColorPreferencesIndicesForTypeParamsReference(); + + if (typeParamsReferenceIndices[typeParamsReferenceIndices.length - 1] > typeParamsReferencesCount) { + new Separator().fill(menu, -1); + } + + for (int i= 0; i < typeParamsReferenceIndices.length; i++) { + if (typeParamsReferenceIndices[i] > typeParamsReferencesCount) { + new ActionContributionItem(new UnusedColorPreferenceMenuItem( + JavadocStylingMessages.JavadocStyling_colorPreferences_typeParameter, typeParamsReferenceIndices[i])) + .fill(menu, -1); + } + } + } + return menu; + } + + @Override + public Menu getMenu(Control parent) { + return null; + } + + @Override + public void dispose() { + if (menu != null) { + menu.dispose(); + menu= null; + } + } + + @Override + public void menuShown(MenuEvent e) { + if (menu != null) { + var parentMenu= ((Menu) e.widget); + // jface creates & displays proxies for sub-menus so just modifying items in sub-menu we return won't work, but we have to remove whole sub-menu item from menu + var menuItem= Stream.of(parentMenu.getItems()) + .filter(mi -> mi.getData() instanceof ActionContributionItem aci && aci.getAction() == this) + .findFirst().orElseThrow(() -> new NoSuchElementException( + "This " + //$NON-NLS-1$ + SignatureStylingColorSubMenuItem.class.getSimpleName() + + " instance not found inside menu being shown")); //$NON-NLS-1$ + // can't be done in menuHidden() since SWT.Selection is fired after SWT.Hide, thus run() action would not be executed since item would be disposed + menuItem.dispose(); + + // re-add the sub-mebu as new menu item + var item= new ActionContributionItem(this); + item.fill(parentMenu, -1); + } + } + + private static final class ResetSignatureStylingColorsPreferencesMenuItem extends Action { + public ResetSignatureStylingColorsPreferencesMenuItem() { + super(JavadocStylingMessages.JavadocStyling_colorPreferences_resetAll); + setId(ResetSignatureStylingColorsPreferencesMenuItem.class.getSimpleName()); + } + + @Override + public void run() { + JavaElementLinks.resetAllColorPreferencesToDefaults(); // triggers call to SignatureStylingMenuToolbarAction.parametersColorChanged() + } + } + + private static final class NoSignatureStylingTypeParametersMenuItem extends Action { + public NoSignatureStylingTypeParametersMenuItem() { + super(JavadocStylingMessages.JavadocStyling_colorPreferences_noTypeParameters); + setId(NoSignatureStylingTypeParametersMenuItem.class.getSimpleName()); + setEnabled(false); + } + } + + private static final class UnusedColorPreferenceMenuItem extends Action { + public UnusedColorPreferenceMenuItem(String textPrefix, int colorIdx) { + super(Messages.format(textPrefix, colorIdx)); + setId(UnusedColorPreferenceMenuItem.class.getSimpleName() + "_" + colorIdx); //$NON-NLS-1$ + setToolTipText(JavadocStylingMessages.JavadocStyling_colorPreferences_unusedTypeParameter); + setEnabled(false); + } + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingMenuToolbarAction.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingMenuToolbarAction.java new file mode 100644 index 00000000000..ca3ece2c462 --- /dev/null +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/javadoc/SignatureStylingMenuToolbarAction.java @@ -0,0 +1,193 @@ +/******************************************************************************* +* Copyright (c) 2024 Jozef Tomek and others. +* +* This program and the accompanying materials +* are made available under the terms of the Eclipse Public License 2.0 +* which accompanies this distribution, and is available at +* https://www.eclipse.org/legal/epl-2.0/ +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Jozef Tomek - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.internal.ui.viewsupport.javadoc; + +import java.util.Objects; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.MenuItem; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.ToolBar; + +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IMenuCreator; + +import org.eclipse.jdt.internal.ui.JavaPluginImages; +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.IStylingConfigurationListener; +import org.eclipse.jdt.internal.ui.viewsupport.MenuVisibilityMenuItemsConfigurer; +import org.eclipse.jdt.internal.ui.viewsupport.MenuVisibilityMenuItemsConfigurer.IMenuVisibilityMenuItemAction; +import org.eclipse.jdt.internal.ui.viewsupport.browser.BrowserTextAccessor; +import org.eclipse.jdt.internal.ui.viewsupport.browser.BrowserTextAccessor.IBrowserContentChangeListener; + +/** + * Toolbar item action for building & presenting javadoc styling menu. + */ +public class SignatureStylingMenuToolbarAction extends Action implements IMenuCreator, IBrowserContentChangeListener, IStylingConfigurationListener { + private final Action[] noStylingActions= { new NoStylingEnhancementsAction() }; + private final Action[] enabledActions; + private final Shell parent; + private final Runnable enhancementsReconfiguredTask; + + private Action[] actions; + protected Menu menu= null; + private boolean enhancementsEnabled= JavaElementLinks.getStylingEnabledPreference(); + + public SignatureStylingMenuToolbarAction(Shell parent, BrowserTextAccessor browserAccessor, Supplier javadocContentSupplier, Runnable enhancementsReconfiguredTask) { + super(JavadocStylingMessages.JavadocStyling_enabledTooltip, IAction.AS_DROP_DOWN_MENU); + Objects.requireNonNull(parent); + setImageDescriptor(JavaPluginImages.DESC_ETOOL_JDOC_HOVER_EDIT); + enabledActions= new Action[] { + new ToggleSignatureTypeParametersColoringAction(), + // widget for following action is being removed and re-added repeatedly, see SignatureStylingColorSubMenuItem.menuShown() + new SignatureStylingColorSubMenuItem(parent, javadocContentSupplier)}; + actions= enabledActions; + setMenuCreator(this); + this.parent= parent; + this.enhancementsReconfiguredTask= enhancementsReconfiguredTask; + presentEnhancementsState(); + setHoverImageDescriptor(null); + setId(SignatureStylingMenuToolbarAction.class.getSimpleName()); + browserAccessor.addContentChangedListener(this); // remove not necessary since lifecycle of this action is the same as that of the browser widget + JavaElementLinks.addStylingConfigurationListener(this); + } + + @Override + public void browserContentChanged(Supplier contentAccessor) { + if (!enhancementsEnabled) { + return; + } + var content= contentAccessor.get(); + if (content != null && !content.isBlank() && content.contains(JavaElementLinks.CHECKBOX_ID_FORMATTIG)) { + reAddActionItems(enabledActions); + } else { + reAddActionItems(noStylingActions); + } + } + + private void reAddActionItems(Action[] newActions) { + if (actions != newActions) { + actions= newActions; + if (menu != null) { + Stream.of(menu.getItems()).forEach(MenuItem::dispose); + addMenuItems(); + } + } + } + + @Override + public Menu getMenu(Control p) { + if (menu == null) { + menu= new Menu(parent); + addMenuItems(); + MenuVisibilityMenuItemsConfigurer.registerForMenu(menu); + } + return menu; + } + + @Override + public Menu getMenu(Menu p) { + return null; + } + + private void addMenuItems() { + Stream.of(actions).forEach(action -> new ActionContributionItem(action).fill(menu, -1)); + } + + @Override + public void dispose() { + if (menu != null) { + menu.dispose(); + } + } + + @Override + public void runWithEvent(Event event) { + enhancementsEnabled = !enhancementsEnabled; + JavaElementLinks.setStylingEnabledPreference(enhancementsEnabled); // triggers call to stylingStateChanged() + } + + private void runEnhancementsReconfiguredTask() { + if (enhancementsReconfiguredTask != null) { + parent.getDisplay().execute(enhancementsReconfiguredTask); + } + } + + private void presentEnhancementsState() { + setImageDescriptor(enhancementsEnabled ? JavaPluginImages.DESC_ETOOL_JDOC_HOVER_EDIT : JavaPluginImages.DESC_DTOOL_JDOC_HOVER_EDIT); + setToolTipText(enhancementsEnabled ? JavadocStylingMessages.JavadocStyling_enabledTooltip : JavadocStylingMessages.JavadocStyling_disabledTooltip); + } + + @Override + public void stylingStateChanged(boolean isEnabled) { + parent.getDisplay().execute(() -> { + enhancementsEnabled= isEnabled; + presentEnhancementsState(); + runEnhancementsReconfiguredTask(); + }); + } + + @Override + public void parametersColoringStateChanged(boolean isEnabled) { + runEnhancementsReconfiguredTask(); + } + + @Override + public void parametersColorChanged() { + runEnhancementsReconfiguredTask(); + } + + public void setup(ToolBar toolbar) { + toolbar.addDisposeListener(e -> JavaElementLinks.removeStylingConfigurationListener(this)); + } + + private class NoStylingEnhancementsAction extends Action { + public NoStylingEnhancementsAction() { + super(JavadocStylingMessages.JavadocStyling_noEnhancements); + setEnabled(false); + } + } + + private static class ToggleSignatureTypeParametersColoringAction extends Action implements IMenuVisibilityMenuItemAction { + + public ToggleSignatureTypeParametersColoringAction() { + super(JavadocStylingMessages.JavadocStyling_typeParamsColoring, IAction.AS_CHECK_BOX); + setId(ToggleSignatureTypeParametersColoringAction.class.getSimpleName()); + showCurentPreference(); + } + + private void showCurentPreference() { + setChecked(JavaElementLinks.getPreferenceForTypeParamsColoring()); + } + + @Override + public void menuShown(MenuEvent e) { + showCurentPreference(); + } + + @Override + public void run() { + super.run(); + JavaElementLinks.setPreferenceForTypeParamsColoring(isChecked()); // triggers call to parametersColoringStateChanged() + } + + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/PreferenceConstants.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/PreferenceConstants.java index ddc32bfd258..171d6868e83 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/PreferenceConstants.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/PreferenceConstants.java @@ -65,6 +65,7 @@ import org.eclipse.jdt.internal.ui.text.java.CompletionProposalComputerRegistry; import org.eclipse.jdt.internal.ui.text.java.ProposalSorterRegistry; import org.eclipse.jdt.internal.ui.text.spelling.SpellCheckEngine; +import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks; /** @@ -4346,6 +4347,9 @@ public static void initializeDefaultValues(IPreferenceStore store) { store.setDefault(EDITOR_JAVA_CODEMINING_SHOW_REFERENCES_ON_METHODS, false); store.setDefault(EDITOR_JAVA_CODEMINING_SHOW_IMPLEMENTATIONS, false); store.setDefault(EDITOR_JAVA_CODEMINING_SHOW_PARAMETER_NAMES, false); + + // Javadoc hover & view + JavaElementLinks.initDefaultPreferences(store); } /** From 63f5ab78009bb8208914a71cb3863c273ab80657 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Wed, 27 Mar 2024 01:30:29 -0400 Subject: [PATCH 12/19] Split try-with-resources indent fix (#1288) * Fix indent of split try-with-resources quick-fix - manually create resources and block contents of new try statement and add indentation manually as well - temporarily ignore AssistQuickFixTest1d8.testSplitTryWithResources tests until jdt.core block indentation fix for ASTRewriteAnalyzer is made --- .../corext/fix/SplitTryResourceFixCore.java | 68 ++++++++++++++----- .../tests/quickfix/AssistQuickFixTest1d8.java | 15 ++-- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SplitTryResourceFixCore.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SplitTryResourceFixCore.java index 42baa836dae..6d2aa678189 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SplitTryResourceFixCore.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/fix/SplitTryResourceFixCore.java @@ -19,7 +19,11 @@ import org.eclipse.text.edits.TextEditGroup; +import org.eclipse.jdt.core.IBuffer; import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.Block; @@ -29,9 +33,11 @@ import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ListRewrite; +import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.internal.corext.dom.ASTNodes; import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; +import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; import org.eclipse.jdt.internal.corext.util.JavaModelUtil; import org.eclipse.jdt.internal.ui.text.correction.CorrectionMessages; @@ -82,31 +88,61 @@ public SplitTryResourceProposalOperation(TryStatement statement, VariableDeclara @Override public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore linkedModel) throws CoreException { - TextEditGroup group= new TextEditGroup("abc"); //$NON-NLS-1$ + TextEditGroup group= null; final ASTRewrite rewrite= cuRewrite.getASTRewrite(); final AST ast= cuRewrite.getAST(); - - + ICompilationUnit cu= cuRewrite.getCu(); + CompilationUnit root= (CompilationUnit) tryStatement.getRoot(); + IBuffer cuBuffer= cu.getBuffer(); List resources= tryStatement.resources(); - int expIndex; - for (expIndex= 0; expIndex < resources.size(); ++expIndex) { - if (resources.get(expIndex) == expression) { - break; + TryStatement newTryStatement= ast.newTryStatement(); + IJavaElement rootElement= cuRewrite.getRoot().getJavaElement(); + String fIndent= "\t"; //$NON-NLS-1$ + if (rootElement != null) { + IJavaProject project= rootElement.getJavaProject(); + if (project != null) { + String tab_option= project.getOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, true); + if (JavaCore.SPACE.equals(tab_option)) { + fIndent= ""; //$NON-NLS-1$ + for (int i= 0; i < CodeFormatterUtil.getTabWidth(project); ++i) { + fIndent += " "; //$NON-NLS-1$ + } + } + } + } + boolean copyResources= false; + String prefix= ""; //$NON-NLS-1$ + for (VariableDeclarationExpression resource : resources) { + if (resource.equals(expression)) { + copyResources= true; + } + if (copyResources) { + int start= root.getExtendedStartPosition(resource); + int length= root.getExtendedLength(resource); + StringBuffer buffer= new StringBuffer(prefix); + buffer.append(cuBuffer.getText(start, length)); + VariableDeclarationExpression newVarExpression= (VariableDeclarationExpression) rewrite.createStringPlaceholder(buffer.toString(), ASTNode.VARIABLE_DECLARATION_EXPRESSION); + newTryStatement.resources().add(newVarExpression); + rewrite.remove(resource, group); + prefix= "\n" + fIndent + fIndent; //$NON-NLS-1$ } } - TryStatement newTryStatement= ast.newTryStatement(); - Block newBlock= ast.newBlock(); - newTryStatement.setBody(newBlock); - ListRewrite listRewrite= rewrite.getListRewrite(newTryStatement, TryStatement.RESOURCES2_PROPERTY); - ListRewrite oldResourcesListRewrite= rewrite.getListRewrite(tryStatement, TryStatement.RESOURCES2_PROPERTY); - listRewrite.insertFirst(oldResourcesListRewrite.createMoveTarget(expression, (ASTNode)oldResourcesListRewrite.getOriginalList().get(resources.size() - 1)), group); Block originalBlock= tryStatement.getBody(); List originalStatements= originalBlock.statements(); - int size= originalStatements.size(); ListRewrite originalBlockListRewrite= rewrite.getListRewrite(originalBlock, Block.STATEMENTS_PROPERTY); + StringBuilder buf= new StringBuilder(); + buf.append("{\n"); //$NON-NLS-1$ + for (Statement s : originalStatements) { + int start= root.getExtendedStartPosition(s); + int length= root.getExtendedLength(s); + String text= cuBuffer.getText(start, length); + buf.append(fIndent).append(text).append("\n"); //$NON-NLS-1$ + rewrite.remove(s, group); + } + buf.append("}"); //$NON-NLS-1$ + Block newBlock= (Block) rewrite.createStringPlaceholder(buf.toString(), ASTNode.BLOCK); + newTryStatement.setBody(newBlock); originalBlockListRewrite.insertFirst(newTryStatement, group); - ListRewrite newBlockListRewrite= rewrite.getListRewrite(newBlock, Block.STATEMENTS_PROPERTY); - newBlockListRewrite.insertLast(originalBlockListRewrite.createMoveTarget(originalStatements.get(0), originalStatements.get(size - 1)), group); } } diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java index a0e684b5bee..f402cf92fff 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java @@ -21,6 +21,7 @@ import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -6766,6 +6767,7 @@ public void testAssignInTryWithResources_04() throws Exception { assertExpectedExistInProposals(proposals, new String[] { expected }); } + @Ignore @Test public void testSplitTryWithResources1() throws Exception { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=530208 IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); @@ -6811,8 +6813,10 @@ public void testSplitTryWithResources1() throws Exception { // https://bugs.ecli " public void foo() {\n" + " try (Reader s = new BufferedReader(new FileReader(\"c.d\"))) {\n" + " try (Reader r = new BufferedReader(new FileReader(\"a.b\"));\n" + - " Reader t = new BufferedReader(new FileReader(\"e.f\"))){r.read();\n" + - " System.out.println(\"abc\");} \n" + + " Reader t = new BufferedReader(new FileReader(\"e.f\"))) {\n" + + " r.read();\n" + + " System.out.println(\"abc\");\n" + + " } \n" + " } catch (FileNotFoundException e) {\n" + " e.printStackTrace();\n" + " } catch (IOException e) {\n" + @@ -6824,6 +6828,7 @@ public void testSplitTryWithResources1() throws Exception { // https://bugs.ecli assertExpectedExistInProposals(proposals, new String[] { expected }); } + @Ignore @Test public void testSplitTryWithResources2() throws Exception { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=530208 IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); @@ -6869,8 +6874,10 @@ public void testSplitTryWithResources2() throws Exception { // https://bugs.ecli " public void foo() {\n" + " try (Reader s = new BufferedReader(new FileReader(\"c.d\"));\n" + " Reader r = new BufferedReader(new FileReader(\"a.b\"))) {\n" + - " try (Reader t = new BufferedReader(new FileReader(\"e.f\"))){r.read();\n" + - " System.out.println(\"abc\");} \n" + + " try (Reader t = new BufferedReader(new FileReader(\"e.f\"))) {\n" + + " r.read();\n" + + " System.out.println(\"abc\");\n" + + " } \n" + " } catch (FileNotFoundException e) {\n" + " e.printStackTrace();\n" + " } catch (IOException e) {\n" + From c44eea90df5ce62fd666638336b7cb0a675ec907 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Thu, 28 Mar 2024 13:19:23 -0400 Subject: [PATCH 13/19] Re-enable split try-with-resources tests now that indenting is fixed (#1289) - fixes #1261 --- .../eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java index f402cf92fff..7d065deffd3 100644 --- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java +++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/quickfix/AssistQuickFixTest1d8.java @@ -21,7 +21,6 @@ import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; @@ -6767,7 +6766,6 @@ public void testAssignInTryWithResources_04() throws Exception { assertExpectedExistInProposals(proposals, new String[] { expected }); } - @Ignore @Test public void testSplitTryWithResources1() throws Exception { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=530208 IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); @@ -6828,7 +6826,6 @@ public void testSplitTryWithResources1() throws Exception { // https://bugs.ecli assertExpectedExistInProposals(proposals, new String[] { expected }); } - @Ignore @Test public void testSplitTryWithResources2() throws Exception { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=530208 IPackageFragment pack1= fSourceFolder.createPackageFragment("test1", false, null); From fe0996dafcb2937d21f369ed572bda29692f02a4 Mon Sep 17 00:00:00 2001 From: Hannes Wellmann Date: Fri, 29 Mar 2024 08:02:43 +0100 Subject: [PATCH 14/19] Fix version-ranges to have exclusive upper-bounds --- org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF index 8bad523c585..9c3af4484f3 100644 --- a/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF +++ b/org.eclipse.jdt.ui.unittest.junit/META-INF/MANIFEST.MF @@ -22,7 +22,7 @@ Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)", org.eclipse.jdt.launching;bundle-version="[3.5.0,4.0.0)", org.eclipse.jdt.debug.ui;bundle-version="[3.3.0,4.0.0)", - org.eclipse.jdt.junit.core;bundle-version="[3.11.200,4.0.0]", + org.eclipse.jdt.junit.core;bundle-version="[3.11.200,4.0.0)", org.eclipse.jdt.junit.runtime;bundle-version="[3.5.0,4.0.0)", org.eclipse.core.variables;bundle-version="[3.2.200,4.0.0)", org.eclipse.jdt.core.manipulation;bundle-version="1.9.0", From 10d2f66f49e1535c18629a998b477e461e876a20 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Tue, 20 Feb 2024 11:49:10 +0100 Subject: [PATCH 15/19] Don't show children for JUnit 5 @TestTemplate tests that ran only once When running JUnit 5 @TestTemplate tests, extra test suites are shown in the JUnit view to represent the tests in the template. Those suites introduce unnecessary noise when the template contains only one test - e.g. when repeating tests that failed. This change adjusts TestSuiteElement.getChildren(), pruning each TestSuiteElement that has a single TestCaseElement child that is also a dynamic test. Fixes https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/945 --- .../junit/model/TestSuiteElement.java | 93 ++++++++++++++++++- .../jdt/internal/junit/ui/TestViewer.java | 8 ++ 2 files changed, 99 insertions(+), 2 deletions(-) diff --git a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/model/TestSuiteElement.java b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/model/TestSuiteElement.java index ec8e7907a57..bcf24b2771d 100644 --- a/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/model/TestSuiteElement.java +++ b/org.eclipse.jdt.junit.core/src/org/eclipse/jdt/internal/junit/model/TestSuiteElement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2017 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -33,6 +33,10 @@ public TestSuiteElement(TestSuiteElement parent, String id, String testName, int @Override public Result getTestResult(boolean includeChildren) { + TestCaseElement child= getSingleDynamicChild(); + if (child != null) { + return child.getStatus().convertToResult(); + } if (includeChildren) { return getStatus().convertToResult(); } else { @@ -47,7 +51,12 @@ public String getSuiteTypeName() { @Override public ITestElement[] getChildren() { - return fChildren.toArray(new ITestElement[fChildren.size()]); + TestElement[] elements= fChildren.toArray(new TestElement[fChildren.size()]); + if (elements.length != 1 || !isSingleDynamicTest(elements[0])) { + return elements; + } + // Filter out if this is a single dynamic test inside a testsuite + return new ITestElement[0]; } public void addChild(TestElement child) { @@ -154,4 +163,84 @@ public String toString() { return "TestSuite: " + getTestName() + " : " + super.toString() + " (" + fChildren.size() + ")"; //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } + private boolean isSingleDynamicTest(TestElement element) { + if (element instanceof TestCaseElement testCase) { + if (testCase.isDynamicTest() && fChildren.size() == 1) { + return true; + } + } + return false; + } + + /** + * If this test suite is a {@code @TestTemplate} test case with a single child, return that child. + * @return The single dynamic test case child or {@code null} if the suite has no children or multiple or non-dynamid children. + */ + public TestCaseElement getSingleDynamicChild() { + try { + if (fChildren.size() == 1) { + TestElement child= fChildren.get(0); + if (isSingleDynamicTest(child)) { + return (TestCaseElement) child; + } + } + } catch (IndexOutOfBoundsException e) { + // don't care, children changed concurrently + } + return null; + } + + @Override + public boolean isComparisonFailure() { + TestCaseElement child= getSingleDynamicChild(); + if (child == null) { + return super.isComparisonFailure(); + } + return child.isComparisonFailure(); + } + + @Override + public boolean isAssumptionFailure() { + TestCaseElement child= getSingleDynamicChild(); + if (child == null) { + return super.isAssumptionFailure(); + } + return child.isAssumptionFailure(); + } + + @Override + public FailureTrace getFailureTrace() { + TestCaseElement child= getSingleDynamicChild(); + if (child == null) { + return super.getFailureTrace(); + } + return child.getFailureTrace(); + } + + @Override + public String getTrace() { + TestCaseElement child= getSingleDynamicChild(); + if (child != null) { + return child.getTrace(); + } + return super.getTrace(); + } + + @Override + public String getExpected() { + TestCaseElement child= getSingleDynamicChild(); + if (child == null) { + return super.getExpected(); + } + return child.getExpected(); + } + + @Override + public String getActual() { + TestCaseElement child= getSingleDynamicChild(); + if (child == null) { + return super.getActual(); + } + return child.getActual(); + } } diff --git a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java index 48082404b49..a982151d7b3 100644 --- a/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java +++ b/org.eclipse.jdt.junit/src/org/eclipse/jdt/internal/junit/ui/TestViewer.java @@ -452,6 +452,14 @@ private OpenTestAction getOpenTestAction(TestSuiteElement testSuite) { // a group of parameterized tests return new OpenTestAction(fTestRunnerPart, (TestCaseElement) children[0], null); } + if (children.length == 0) { + // check if we have applied the workaround for: https://github.com/eclipse-jdt/eclipse.jdt.ui/issues/945 + TestCaseElement child= testSuite.getSingleDynamicChild(); + if (child != null) { + // a parameterized test that ran only one test + return new OpenTestAction(fTestRunnerPart, child, null); + } + } int index= testName.indexOf('('); // test factory method From c25d5dbcc0e1c43ad59eca937e97902c3ed4dbd9 Mon Sep 17 00:00:00 2001 From: Jozef Tomek Date: Fri, 29 Mar 2024 21:59:36 +0100 Subject: [PATCH 16/19] Do not log locking CSS fragments cache for read was interrupted (#1294) --- .../jdt/internal/ui/viewsupport/JavaElementLinks.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java index 59780fc5cfa..e8c46f48289 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java @@ -562,8 +562,8 @@ private static void cssFragmentsCacheResetListener(PropertyChangeEvent event) { CSS_FRAGMENTS_CACHE_LOCK.unlock(); } } - } catch (InterruptedException e1) { - JavaPlugin.logErrorMessage("Interrupted while waiting for CSS fragments cache lock, cache reset unsuccessful"); //$NON-NLS-1$ + } catch (InterruptedException e) { + JavaPlugin.log(new RuntimeException("Interrupted while waiting for CSS fragments cache lock, cache reset unsuccessful", e)); //$NON-NLS-1$ } } } @@ -866,7 +866,8 @@ public static String modifyCssStyleSheet(String css, StringBuilder buffer) { try { locked= CSS_FRAGMENTS_CACHE_LOCK.tryLock(100, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { - JavaPlugin.logErrorMessage("Interrupted while waiting for CSS fragments cache lock, proceeding without using cache"); //$NON-NLS-1$ + Thread.currentThread().interrupt(); + return css; } try { if (locked) { From fef727f37aa694aa1071101095add6b7c6128a53 Mon Sep 17 00:00:00 2001 From: Jeff Johnston Date: Tue, 2 Apr 2024 22:02:23 -0400 Subject: [PATCH 17/19] Fix extraction of yield statements into method (#1308) * Fix extraction of yield statements into method - add YieldStatement support to FlowAnalyzer and InOutFlowAnalyzer - add code to ExtractMethodRefactoring to change exposed yield statements that are moved to extracted method into return statements and replace the extracted code with a yield statement that calls the new method - fixes #1286 --- .../refactoring/code/flow/FlowAnalyzer.java | 20 ++++ .../code/flow/InOutFlowAnalyzer.java | 16 +++- .../code/flow/InputFlowAnalyzer.java | 15 ++- .../refactoring/code/flow/ReturnFlowInfo.java | 7 +- .../code/ExtractMethodRefactoring.java | 92 +++++++++++++++++-- 5 files changed, 141 insertions(+), 9 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java index 1168cc6a86a..131c2ebe102 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/FlowAnalyzer.java @@ -107,6 +107,7 @@ import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.WhileStatement; import org.eclipse.jdt.core.dom.WildcardType; +import org.eclipse.jdt.core.dom.YieldStatement; import org.eclipse.jdt.internal.corext.dom.GenericVisitor; @@ -154,6 +155,7 @@ public FlowAnalyzer(FlowContext context) { } protected abstract boolean createReturnFlowInfo(ReturnStatement node); + protected abstract boolean createReturnFlowInfo(YieldStatement node); protected abstract boolean traverseNode(ASTNode node); @@ -172,6 +174,10 @@ protected ReturnFlowInfo createReturn(ReturnStatement statement) { return new ReturnFlowInfo(statement); } + protected ReturnFlowInfo createReturn(YieldStatement statement) { + return new ReturnFlowInfo(statement); + } + protected ThrowFlowInfo createThrow() { return new ThrowFlowInfo(); } @@ -849,6 +855,20 @@ public void endVisit(ReturnStatement node) { } } + @Override + public void endVisit(YieldStatement node) { + if (skipNode(node)) + return; + + if (createReturnFlowInfo(node)) { + ReturnFlowInfo info= createReturn(node); + setFlowInfo(node, info); + info.merge(getFlowInfo(node.getExpression())); + } else { + assignFlowInfo(node, node.getExpression()); + } + } + @Override public void endVisit(SimpleName node) { if (skipNode(node) || node.isDeclaration()) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java index eae6a5913a3..24b2e16a3ef 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InOutFlowAnalyzer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -26,12 +26,16 @@ import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.dom.ReturnStatement; import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.SwitchExpression; import org.eclipse.jdt.core.dom.VariableDeclarationExpression; import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; +import org.eclipse.jdt.core.dom.YieldStatement; public class InOutFlowAnalyzer extends FlowAnalyzer { + private ASTNode[] fSelectedNodes= null; + public InOutFlowAnalyzer(FlowContext context) { super(context); } @@ -39,6 +43,7 @@ public InOutFlowAnalyzer(FlowContext context) { public FlowInfo perform(ASTNode[] selectedNodes) { FlowContext context= getFlowContext(); GenericSequentialFlowInfo result= createSequential(); + fSelectedNodes= selectedNodes; for (ASTNode node : selectedNodes) { node.accept(this); result.merge(getFlowInfo(node), context); @@ -58,6 +63,15 @@ protected boolean createReturnFlowInfo(ReturnStatement node) { return true; } + @Override + protected boolean createReturnFlowInfo(YieldStatement node) { + ASTNode parent= node.getParent(); + while (parent != null && !(parent instanceof SwitchExpression)) { + parent= parent.getParent(); + } + return (parent instanceof SwitchExpression) && fSelectedNodes.length > 0 && parent.getStartPosition() < fSelectedNodes[0].getStartPosition(); // we are only traversing selected nodes. + } + @Override public void endVisit(Block node) { super.endVisit(node); diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java index 25f7b159875..1220dd512d4 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/InputFlowAnalyzer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2023 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -39,6 +39,7 @@ import org.eclipse.jdt.core.dom.SwitchExpression; import org.eclipse.jdt.core.dom.SwitchStatement; import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.YieldStatement; import org.eclipse.jdt.internal.corext.dom.Selection; @@ -61,6 +62,11 @@ protected boolean createReturnFlowInfo(ReturnStatement node) { // Make sure that the whole return statement is selected or located before the selection. return node.getStartPosition() + node.getLength() <= fSelection.getExclusiveEnd(); } + @Override + protected boolean createReturnFlowInfo(YieldStatement node) { + // Make sure that the whole return statement is selected or located before the selection. + return node.getStartPosition() + node.getLength() <= fSelection.getExclusiveEnd(); + } public void process(ASTNode node) { try { fFlowContext.setLoopReentranceMode(true); @@ -169,6 +175,13 @@ protected boolean createReturnFlowInfo(ReturnStatement node) { return node.getStartPosition() >= fSelection.getInclusiveEnd(); } + @Override + protected boolean createReturnFlowInfo(YieldStatement node) { + // Make sure that the whole return statement is located after the selection. There can be cases like + // return i + [x + 10] * 10; In this case we must not create a return info node. + return node.getStartPosition() >= fSelection.getInclusiveEnd(); + } + @Override public void endVisit(ConditionalExpression node) { if (skipNode(node)) diff --git a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java index 5772392400a..4a7e65bde85 100644 --- a/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java +++ b/org.eclipse.jdt.core.manipulation/core extension/org/eclipse/jdt/internal/corext/refactoring/code/flow/ReturnFlowInfo.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -16,6 +16,7 @@ import org.eclipse.jdt.core.dom.Expression; import org.eclipse.jdt.core.dom.ReturnStatement; +import org.eclipse.jdt.core.dom.YieldStatement; class ReturnFlowInfo extends FlowInfo { @@ -23,6 +24,10 @@ public ReturnFlowInfo(ReturnStatement node) { super(getReturnFlag(node)); } + public ReturnFlowInfo(@SuppressWarnings("unused") YieldStatement node) { + super(VALUE_RETURN); + } + public void merge(FlowInfo info) { if (info == null) return; diff --git a/org.eclipse.jdt.core.manipulation/refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java b/org.eclipse.jdt.core.manipulation/refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java index 6af1419fafe..ab1b4b30b50 100644 --- a/org.eclipse.jdt.core.manipulation/refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java +++ b/org.eclipse.jdt.core.manipulation/refactoring/org/eclipse/jdt/internal/corext/refactoring/code/ExtractMethodRefactoring.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2021 IBM Corporation and others. + * Copyright (c) 2000, 2024 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -41,6 +41,7 @@ import org.eclipse.jface.text.Document; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.Refactoring; @@ -91,6 +92,7 @@ import org.eclipse.jdt.core.dom.SingleVariableDeclaration; import org.eclipse.jdt.core.dom.Statement; import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; +import org.eclipse.jdt.core.dom.SwitchExpression; import org.eclipse.jdt.core.dom.ThisExpression; import org.eclipse.jdt.core.dom.Type; import org.eclipse.jdt.core.dom.TypeDeclaration; @@ -99,6 +101,7 @@ import org.eclipse.jdt.core.dom.VariableDeclarationFragment; import org.eclipse.jdt.core.dom.VariableDeclarationStatement; import org.eclipse.jdt.core.dom.WhileStatement; +import org.eclipse.jdt.core.dom.YieldStatement; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext; @@ -176,6 +179,7 @@ public class ExtractMethodRefactoring extends Refactoring { private ASTNode[] fDestinations; private LinkedProposalModelCore fLinkedProposalModel; private Map fFormatterOptions; + private boolean fHasYield; private static final String EMPTY= ""; //$NON-NLS-1$ @@ -964,6 +968,58 @@ private String getType(VariableDeclaration declaration, boolean isVarargs, boole //---- Code generation ----------------------------------------------------------------------- + private class YieldStatementCheckerReplacer extends ASTVisitor { + private boolean hasYield; + private boolean hasReturn; + private final IRegion fSelectedRange; + private final ASTRewrite fReplaceRewriter; + + public YieldStatementCheckerReplacer(IRegion selectedRange) { + this.fSelectedRange= selectedRange; + this.fReplaceRewriter= null; + } + + public YieldStatementCheckerReplacer(ASTRewrite rewriter, IRegion selectedRange) { + this.fSelectedRange= selectedRange; + this.fReplaceRewriter= rewriter; + } + + public boolean hasYield() { + return hasYield; + } + + public boolean hasReturn() { + return hasReturn; + } + @Override + public boolean visit(YieldStatement node) { + if (node.getStartPosition() < fSelectedRange.getOffset() + fSelectedRange.getLength()) { + ASTNode parent= node.getParent(); + if (parent != null) { + while (parent != null && !(parent instanceof SwitchExpression)) { + parent= parent.getParent(); + } + if (parent.getStartPosition() < fSelectedRange.getOffset()) { + hasYield= true; + if (fReplaceRewriter != null) { + ReturnStatement rs= fReplaceRewriter.getAST().newReturnStatement(); + rs.setExpression((Expression)fReplaceRewriter.createCopyTarget(node.getExpression())); + fReplaceRewriter.replace(node, rs, null); + } + } + } + } + return true; + } + @Override + public boolean visit(ReturnStatement node) { + if (node.getStartPosition() < fSelectedRange.getOffset() + fSelectedRange.getLength()) { + hasReturn= true; + } + return true; + } + } + private ASTNode[] createCallNodes(SnippetFinder.Match duplicate, int modifiers) { List result= new ArrayList<>(2); @@ -1017,8 +1073,19 @@ private ASTNode[] createCallNodes(SnippetFinder.Match duplicate, int modifiers) } break; case ExtractMethodAnalyzer.RETURN_STATEMENT_VALUE: - ReturnStatement rs= fAST.newReturnStatement(); - rs.setExpression(invocation); + YieldStatementCheckerReplacer yieldChecker= new YieldStatementCheckerReplacer(fAnalyzer.getSelectedNodeRange()); + for (ASTNode node : fAnalyzer.getSelectedNodes()) { + node.accept(yieldChecker); + } + Statement rs= null; + if (yieldChecker.hasYield() && !yieldChecker.hasReturn()) { + rs= fAST.newYieldStatement(); + ((YieldStatement)rs).setExpression(invocation); + fHasYield= true; + } else { + rs= fAST.newReturnStatement(); + ((ReturnStatement)rs).setExpression(invocation); + } call= rs; break; default: @@ -1263,12 +1330,19 @@ private Block createMethodBody(ASTNode[] selectedNodes, TextEditGroup substitute fAnalyzer.getReturnTypeBinding().equals(fAST.resolveWellKnownType("void")); //$NON-NLS-1$ if (selectedNodes.length == 1) { if (!isReturnVoid) { + if (fHasYield) { + YieldStatementCheckerReplacer yieldChecker= new YieldStatementCheckerReplacer(fRewriter, fAnalyzer.getSelectedNodeRange()); + selectedNodes[0].accept(yieldChecker); + } if (selectedNodes[0] instanceof Block) { Block block= (Block)selectedNodes[0]; + ListRewrite source= fRewriter.getListRewrite( + block, + Block.STATEMENTS_PROPERTY); List blockStatements= block.statements(); - for (Statement blockStatement : blockStatements) { - statements.insertLast(fRewriter.createMoveTarget(blockStatement), substitute); - } + ASTNode toMove= source.createMoveTarget( + blockStatements.get(0), blockStatements.get(blockStatements.size() - 1)); + statements.insertLast(toMove, substitute); } else { statements.insertLast(fRewriter.createMoveTarget(selectedNodes[0]), substitute); } @@ -1291,6 +1365,12 @@ private Block createMethodBody(ASTNode[] selectedNodes, TextEditGroup substitute (ChildListPropertyDescriptor) selectedNodes[0].getLocationInParent()); // if last statement is a void return statement then we skip it int index= isReturnVoid ? selectedNodes.length - 2 : selectedNodes.length - 1; + if (fHasYield) { + for (ASTNode selectedNode : selectedNodes) { + YieldStatementCheckerReplacer yieldChecker= new YieldStatementCheckerReplacer(fRewriter, fAnalyzer.getSelectedNodeRange()); + selectedNode.accept(yieldChecker); + } + } ASTNode toMove= source.createMoveTarget( selectedNodes[0], selectedNodes[index], replacementNode, substitute); From a0babe0e7a89e36d53f17b9c1da0b0d3fe867edf Mon Sep 17 00:00:00 2001 From: Jozef Tomek <36334098+RedeemerSK@users.noreply.github.com> Date: Wed, 3 Apr 2024 04:08:42 +0200 Subject: [PATCH 18/19] Fix type parameters coloring corner cases (#1073 #1074) (#1296) Missing coloring for class / interface from other compilation unit. Applied coloring to labels outside of diamond brackets. --- .../JavaElementLabelComposerCore.java | 15 ++++++++++++--- .../internal/ui/viewsupport/JavaElementLinks.java | 7 ++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java index 6775c7326fc..f5c72d8700c 100644 --- a/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java +++ b/org.eclipse.jdt.core.manipulation/common/org/eclipse/jdt/internal/core/manipulation/JavaElementLabelComposerCore.java @@ -904,19 +904,28 @@ protected void appendTypeParameterSignaturesLabel(String[] typeParamSigs, long f if (i > 0) { fBuffer.append(JavaElementLabelsCore.COMMA_STRING); } - fBuffer.append(Signature.getTypeVariable(typeParamSigs[i])); + appendTypeParameteSignatureLabel(Signature.getTypeVariable(typeParamSigs[i])); } appendGT(); } } + /** + * Appends label for single type parameter from a signature. + * + * @param typeVariableName the type variable name + */ + protected void appendTypeParameteSignatureLabel(String typeVariableName) { + fBuffer.append(typeVariableName); + } + /** * Appends the string for rendering the '<' character. */ protected void appendLT() { fBuffer.append(getLT()); } - + /** * Returns the string for rendering the '<' character. * @@ -932,7 +941,7 @@ protected String getLT() { protected void appendGT() { fBuffer.append(getGT()); } - + /** * Returns the string for rendering the '>' character. * diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java index e8c46f48289..69499fd5ffd 100644 --- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java +++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java @@ -451,7 +451,7 @@ protected void appendMethodParamName(String name) { @Override protected void appendTypeParameterWithBounds(ITypeParameter typeParameter, long flags) throws JavaModelException { - if (noEnhancements) { + if (noEnhancements || nextNestingLevel == 1) { super.appendTypeParameterWithBounds(typeParameter, flags); } else { fBuffer.append("