Bug 538952: [*-Editor] Rework assistants

  - Adapt to changes in ltk
  - Add support for substring matching to content assistant
diff --git a/r/org.eclipse.statet.r.core-tests/src/org/eclipse/statet/r/core/RSearchPatternTest.java b/r/org.eclipse.statet.r.core-tests/src/org/eclipse/statet/r/core/RSearchPatternTest.java
new file mode 100644
index 0000000..f1c2d82
--- /dev/null
+++ b/r/org.eclipse.statet.r.core-tests/src/org/eclipse/statet/r/core/RSearchPatternTest.java
@@ -0,0 +1,757 @@
+/*=============================================================================#
+ # Copyright (c) 2019 Stephan Wahlbrink <sw@wahlbrink.eu> and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.core;
+
+import static org.eclipse.statet.jcommons.text.core.SearchPattern.PREFIX_MATCH;
+import static org.eclipse.statet.jcommons.text.core.SearchPattern.SUBSTRING_MATCH;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.text.core.SearchPattern;
+
+
+@NonNullByDefault
+public class RSearchPatternTest {
+	// Copy of org.eclipse.statet.jcommons.text.core.SearchPatternTests
+	
+	
+	public RSearchPatternTest() {
+	}
+	
+	
+	protected RSearchPattern createPattern(final int rules) {
+		return new RSearchPattern(rules, "");
+	}
+	
+	
+	@Test
+	public void matches_Prefix() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(0, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+	}
+	
+	@Test
+	public void matches_Prefix_CaseInsensitive() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(0, pattern.matches("xAbcxabc"));
+		
+		pattern.setPattern("A");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(0, pattern.matches("xAbcxAbc"));
+		assertEquals(0, pattern.matches("xABcxABc"));
+		assertEquals(0, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("Ab");
+		assertEquals(0, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xAbcxAbc"));
+		assertEquals(0, pattern.matches("xABcxABc"));
+		assertEquals(0, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("aB");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xAbcxAbc"));
+		assertEquals(0, pattern.matches("xABcxABc"));
+		assertEquals(0, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(0, pattern.matches("Abc"));
+		assertEquals(0, pattern.matches("aBc"));
+		assertEquals(0, pattern.matches("abC"));
+		assertEquals(0, pattern.matches("xAbcxAbc"));
+		assertEquals(0, pattern.matches("xaBcxaBc"));
+		assertEquals(0, pattern.matches("xabCxabC"));
+	}
+	
+	@Test
+	public void matches_Prefix_StartingDot() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(0, pattern.matches(".a"));
+		
+		pattern.setPattern(".");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("."));
+		assertEquals(PREFIX_MATCH, pattern.matches(".a"));
+		
+		pattern.setPattern("a");
+		assertEquals(0, pattern.matches(".a"));
+		assertEquals(0, pattern.matches(".abc"));
+		assertEquals(0, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches(".xbc"));
+		
+		pattern.setPattern(".a");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(0, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		assertEquals(PREFIX_MATCH, pattern.matches(".a"));
+		assertEquals(PREFIX_MATCH, pattern.matches(".abc"));
+		assertEquals(0, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches(".xbc"));
+		assertEquals(0, pattern.matches("a."));
+		assertEquals(0, pattern.matches("a.bc"));
+		assertEquals(0, pattern.matches("x.abcxabc"));
+		assertEquals(0, pattern.matches("x.bc"));
+	}
+	
+	@Test
+	public void matches_Prefix_InnerDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(0, pattern.matches("x.abcxabc"));
+		assertEquals(0, pattern.matches("x.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("x_abcxabc"));
+		assertEquals(0, pattern.matches("x_bc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ab.c"));
+		assertEquals(0, pattern.matches("x.abcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		assertEquals(0, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ab_c"));
+		assertEquals(0, pattern.matches("x_abcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(0, pattern.matches("a.bc"));
+		assertEquals(0, pattern.matches("a.b.c"));
+		assertEquals(0, pattern.matches("x.a.b.c.x.a.b.c"));
+		assertEquals(0, pattern.matches("xbc"));
+		assertEquals(0, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("a_b.c"));
+		assertEquals(0, pattern.matches("x_a_b_c_x_a_b_c"));
+		assertEquals(0, pattern.matches("xbc"));
+	}
+	
+	@Test
+	public void matches_Prefix_EndingDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		
+		pattern.setPattern("a.");
+		assertEquals(0, pattern.matches(".a"));
+		assertEquals(0, pattern.matches(".abc"));
+		assertEquals(0, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(0, pattern.matches("xa.bcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("xa_bcxabc"));
+		
+		pattern.setPattern("a_");
+		assertEquals(0, pattern.matches(".a"));
+		assertEquals(0, pattern.matches(".abc"));
+		assertEquals(0, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(0, pattern.matches("xa.bcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("xa_bcxabc"));
+	}
+	
+	@Test
+	public void matches_Substring() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xbc"));
+		assertEquals(0, pattern.matches("xbxc"));
+	}
+	
+	@Test
+	public void matches_Substring_CaseInsensitive() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xAbcxabc"));
+		
+		pattern.setPattern("A");
+		assertEquals(PREFIX_MATCH, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xAbcxAbc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xABcxABc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("Ab");
+		assertEquals(0, pattern.matches("A"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xAbcxAbc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xABcxABc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("aB");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("Abc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ABc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("aBc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xAbcxAbc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xABcxABc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xaBcxaBc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(SUBSTRING_MATCH, pattern.matches("Abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("aBc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("abC"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xAbcxAbc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xaBcxaBc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabCxabC"));
+	}
+	
+	@Test
+	public void matches_Substring_StartingDot() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(0, pattern.matches(".a"));
+		
+		pattern.setPattern(".");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("."));
+		assertEquals(PREFIX_MATCH, pattern.matches(".a"));
+		
+		pattern.setPattern("a");
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".a"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches(".xbc"));
+		
+		pattern.setPattern(".a");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(0, pattern.matches("abc"));
+		assertEquals(0, pattern.matches("xabcxabc"));
+		assertEquals(0, pattern.matches("xbc"));
+		assertEquals(PREFIX_MATCH, pattern.matches(".a"));
+		assertEquals(PREFIX_MATCH, pattern.matches(".abc"));
+		assertEquals(0, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches(".xbc"));
+		assertEquals(0, pattern.matches("a."));
+		assertEquals(0, pattern.matches("a.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x.abcxabc"));
+		assertEquals(0, pattern.matches("x.bc"));
+	}
+	
+	@Test
+	public void matches_Substring_InnerDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x.abcxabc"));
+		assertEquals(0, pattern.matches("x.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x_abcxabc"));
+		assertEquals(0, pattern.matches("x_bc"));
+		
+		pattern.setPattern("ab");
+		assertEquals(0, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ab.c"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x.abcx.abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa.bcxa.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xab.cxab.c"));
+		assertEquals(0, pattern.matches("xbc"));
+		assertEquals(0, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("ab_c"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x_abcx_abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa_bcxa_bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xab_cxab_c"));
+		assertEquals(0, pattern.matches("xbc"));
+		
+		pattern.setPattern("bc");
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("a.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("a.b.c"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x.a.b.c."));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("a_bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("a_b.c"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("x_a_b_c_"));
+	}
+	
+	@Test
+	public void matches_Substring_EndingDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		
+		pattern.setPattern("a");
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		
+		pattern.setPattern("a.");
+		assertEquals(0, pattern.matches(".a"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("xa"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa."));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa_"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa_bc"));
+		
+		pattern.setPattern("a_");
+		assertEquals(0, pattern.matches(".a"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches(".xabcxabc"));
+		assertEquals(0, pattern.matches("a"));
+		assertEquals(PREFIX_MATCH, pattern.matches("abc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xabcxabc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a."));
+		assertEquals(PREFIX_MATCH, pattern.matches("a.bc"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_"));
+		assertEquals(PREFIX_MATCH, pattern.matches("a_bc"));
+		assertEquals(0, pattern.matches("xa"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa."));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa.bc"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa_"));
+		assertEquals(SUBSTRING_MATCH, pattern.matches("xa_bc"));
+	}
+	
+	
+	@Test
+	public void getMatchingRegions_Prefix() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("");
+		assertNull(
+				pattern.getMatchingRegions("a", PREFIX_MATCH) );
+		assertNull(
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Prefix_CaseInsensitive() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("A", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("Abc", PREFIX_MATCH) );
+		
+		pattern.setPattern("A");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("Abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("aBc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("ABc", PREFIX_MATCH) );
+		
+		pattern.setPattern("Ab");
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("aBc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("ABc", PREFIX_MATCH) );
+		
+		pattern.setPattern("aB");
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("Abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("ABc", PREFIX_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Prefix_StartingDot() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern(".");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions(".", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions(".a", PREFIX_MATCH) );
+		
+		pattern.setPattern(".a");
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions(".a", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions(".abc", PREFIX_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Prefix_InnerDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a.", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a.bc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a_", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a_bc", PREFIX_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 0, 3 },
+				pattern.getMatchingRegions("a.bc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("ab.c", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 3 },
+				pattern.getMatchingRegions("a_bc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("ab_c", PREFIX_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Prefix_EndingDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(PREFIX_MATCH);
+		
+		pattern.setPattern("");
+		assertNull(
+				pattern.getMatchingRegions("a.", PREFIX_MATCH) );
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a.", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a_", PREFIX_MATCH) );
+		
+		pattern.setPattern("a.");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("a.", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("a.bc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a_", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a_bc", PREFIX_MATCH) );
+		
+		pattern.setPattern("a_");
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("abc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a.", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 1 },
+				pattern.getMatchingRegions("a.bc", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("a_", PREFIX_MATCH) );
+		assertArrayEquals(new int[] { 0, 2 },
+				pattern.getMatchingRegions("a_bc", PREFIX_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Substring() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("bc");
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4, 6, 8 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xbc", SUBSTRING_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Substring_CaseInsensitive() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xAbcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxAbc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xAbcxAbc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("A");
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xAbcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxAbc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xAbcxaBc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xABcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("Ab");
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xAbcxaBc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xABcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("aB");
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xAbcxaBc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3, 5, 7 },
+				pattern.getMatchingRegions("xABcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("bc");
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4, 6, 8 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xbc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("Bc");
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("abC", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4, 6, 8 },
+				pattern.getMatchingRegions("xabcxabC", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xbC", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("bC");
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("aBc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4, 6, 8 },
+				pattern.getMatchingRegions("xabcxaBc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xBc", SUBSTRING_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Substring_StartingDot() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions(".a", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions(".abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 3, 6, 7 },
+				pattern.getMatchingRegions(".xabcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern(".a");
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("x.abcxabc", SUBSTRING_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Substring_InnerDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a");
+		assertArrayEquals(new int[] { 2, 3, 6, 7 },
+				pattern.getMatchingRegions("x.abcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 3, 6, 7 },
+				pattern.getMatchingRegions("x_abcxabc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("ab");
+		assertArrayEquals(new int[] { 2, 4, 7, 9 },
+				pattern.getMatchingRegions("x.abcx.abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 4, 6, 9 },
+				pattern.getMatchingRegions("xa.bcxa.bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3, 6, 8 },
+				pattern.getMatchingRegions("xab.cxab.c", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4, 7, 9 },
+				pattern.getMatchingRegions("x_abcx_abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 4, 6, 9 },
+				pattern.getMatchingRegions("xa_bcxa_bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3, 6, 8 },
+				pattern.getMatchingRegions("xab_cxab_c", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("bc");
+		assertArrayEquals(new int[] { 2, 4 },
+				pattern.getMatchingRegions("a.bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 5 },
+				pattern.getMatchingRegions("a.b.c", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 4, 7 },
+				pattern.getMatchingRegions("x.a.b.c.", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 4 },
+				pattern.getMatchingRegions("a_bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 5 },
+				pattern.getMatchingRegions("a_b.c", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 4, 7 },
+				pattern.getMatchingRegions("x_a_b_c_", SUBSTRING_MATCH) );
+	}
+	
+	@Test
+	public void getMatchingRegions_Substring_EndingDotAndUnderscore() {
+		final SearchPattern pattern= createPattern(SUBSTRING_MATCH);
+		
+		pattern.setPattern("a.");
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions(".abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 3, 6, 7 },
+				pattern.getMatchingRegions(".xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xa.", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xa.bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions("xa_", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions("xa_bc", SUBSTRING_MATCH) );
+		
+		pattern.setPattern("a_");
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions(".abc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 2, 3, 6, 7 },
+				pattern.getMatchingRegions(".xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2, 5, 6 },
+				pattern.getMatchingRegions("xabcxabc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions("xa.", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 2 },
+				pattern.getMatchingRegions("xa.bc", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xa_", SUBSTRING_MATCH) );
+		assertArrayEquals(new int[] { 1, 3 },
+				pattern.getMatchingRegions("xa_bc", SUBSTRING_MATCH) );
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RSearchPattern.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RSearchPattern.java
new file mode 100644
index 0000000..1ddf9d2
--- /dev/null
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/RSearchPattern.java
@@ -0,0 +1,368 @@
+/*=============================================================================#
+ # Copyright (c) 2016, 2019 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.r.core;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.text.core.SearchPattern;;
+
+
+@NonNullByDefault
+public class RSearchPattern extends SearchPattern {
+	
+	
+	private static final int NOT_CHECKED= -1;
+	
+	
+	private int minCharCount;
+	
+	
+	public RSearchPattern(final int rules, final String pattern) {
+		super(rules, pattern);
+	}
+	
+	
+	@Override
+	protected void onPatternChanged(final String pattern) {
+		this.minCharCount= NOT_CHECKED;
+	}
+	
+	
+	private int getR1MinCharCount() {
+		int count= this.minCharCount;
+		if (count == NOT_CHECKED) {
+			final char[] pattern= getPatternChars();
+			if (pattern.length > 0) {
+				count= 1;
+				for (int i= 1; i < pattern.length; i++) {
+					final char c= pattern[i];
+					if (c != '.' && c != '_') {
+						count++;
+					}
+				}
+			}
+			else {
+				count= 0;
+			}
+			this.minCharCount= count;
+		}
+		return count;
+	}
+	
+	
+	@Override
+	public int matches(final String name) {
+		final int allowedRules= getRules();
+		final char[] patternChars= getPatternChars();
+		char[] nameChars= null;
+		
+		final int minCharCount= getR1MinCharCount();
+		final int lDiff= name.length() - minCharCount;
+		if (lDiff >= 0) {
+			if ((allowedRules & PREFIX_MATCH) != 0
+					&& (isR1PrefixMatch(patternChars, 0, patternChars.length,
+									name, 0, name.length() ) )) {
+				return PREFIX_MATCH;
+			}
+			if (lDiff > 0 && patternChars.length > 0) {
+				if ((allowedRules & SUBSTRING_MATCH) != 0
+						&& isR1SubstringMatch(patternChars, 0, patternChars.length, minCharCount,
+								nameChars= getNameChars(name), 1, nameChars.length )) {
+					return SUBSTRING_MATCH;
+				}
+			}
+		}
+		return 0;
+	}
+	
+	
+	protected boolean isR1PrefixMatch(final char[] pattern, final int patternStart, final int patternEnd,
+			final String name, final int nameStart, final int nameEnd) {
+		if (nameEnd - nameStart == 0) {
+			return false;
+		}
+		
+		if (patternEnd - patternStart == 0) {
+			return (name.charAt(nameStart) != '.');
+		}
+		char patternChar= pattern[patternStart];
+		char nameChar= Character.toLowerCase(name.charAt(nameStart));
+		if (nameChar != patternChar) {
+			return false;
+		}
+		
+		int patternIdx= patternStart;
+		int nameIdx= nameStart;
+		while (true) {
+			if (nameChar == patternChar) {
+				if (++patternIdx >= patternEnd) {
+					return true;
+				}
+				if (++nameIdx < nameEnd) {
+					patternChar= pattern[patternIdx];
+					nameChar= Character.toLowerCase(name.charAt(nameIdx));
+					continue;
+				}
+			}
+			else if (patternChar == '.' || patternChar == '_') {
+				if (++patternIdx >= patternEnd) {
+					return true;
+				}
+				patternChar= pattern[patternIdx];
+				continue;
+			}
+			else if (nameChar == '.' || nameChar == '_') {
+				if (++nameIdx < nameEnd) {
+					nameChar= Character.toLowerCase(name.charAt(nameIdx));
+					continue;
+				}
+			}
+			return false;
+		}
+	}
+	
+	protected boolean isR1PrefixMatch(final char[] pattern, final int patternStart, final int patternEnd,
+			final char[] name, final int nameStart, final int nameEnd) {
+		if (nameEnd - nameStart == 0) {
+			return false;
+		}
+		
+		if (patternEnd - patternStart == 0) {
+			return (name[nameStart] != '.');
+		}
+		char patternChar= pattern[patternStart];
+		char nameChar= name[nameStart];
+		if (nameChar != patternChar) {
+			return false;
+		}
+		
+		int patternIdx= patternStart;
+		int nameIdx= nameStart;
+		while (true) {
+			if (nameChar == patternChar) {
+				if (++patternIdx >= patternEnd) {
+					return true;
+				}
+				if (++nameIdx < nameEnd) {
+					patternChar= pattern[patternIdx];
+					nameChar= name[nameIdx];
+					continue;
+				}
+			}
+			else if (patternChar == '.' || patternChar == '_') {
+				if (++patternIdx >= patternEnd) {
+					return true;
+				}
+				patternChar= pattern[patternIdx];
+				continue;
+			}
+			else if (nameChar == '.' || nameChar == '_') {
+				if (++nameIdx < nameEnd) {
+					nameChar= name[nameIdx];
+					continue;
+				}
+			}
+			return false;
+		}
+	}
+	
+	protected boolean isR1SubstringMatch(final char[] pattern, final int patternStart, final int patternEnd,
+			final int minCharCount,
+			final char[] name, final int nameStart, final int nameEnd) {
+		final int nameLastStart= nameEnd - minCharCount;
+		ITER_SUB: for (int nameCurrentStart= nameStart; nameCurrentStart <= nameLastStart; ) {
+			int patternIdx= patternStart;
+			int nameIdx= nameCurrentStart;
+			
+			char patternChar= pattern[patternIdx];
+			char nameChar= name[nameIdx];
+			if (nameChar == patternChar) {
+				while (true) {
+					if (nameChar == patternChar) {
+						if (++patternIdx >= patternEnd) {
+							return true;
+						}
+						if (++nameIdx < nameEnd) {
+							patternChar= pattern[patternIdx];
+							nameChar= name[nameIdx];
+							continue;
+						}
+					}
+					else if (patternChar == '.' || patternChar == '_') {
+						if (++patternIdx >= patternEnd) {
+							return true;
+						}
+						patternChar= pattern[patternIdx];
+						continue;
+					}
+					else if (nameChar == '.' || nameChar == '_') {
+						if (++nameIdx < nameEnd) {
+							nameChar= name[nameIdx];
+							continue;
+						}
+					}
+					break;
+				}
+			}
+			nameCurrentStart++;
+			continue ITER_SUB;
+		}
+		return false;
+	}
+	
+	
+	@Override
+	public int @Nullable [] getMatchingRegions(final String name, final int matchRule) {
+		final char[] patternChars;
+		final char[] nameChars;
+		
+		switch (matchRule) {
+		case EXACT_MATCH:
+		case PREFIX_MATCH:
+			return getR1PrefixMatches(patternChars= getPatternChars(), 0, patternChars.length,
+					name, 0, name.length() );
+		case SUBSTRING_MATCH:
+			this.tmpRegions.clear();
+			addR1SubstringMatches(patternChars= getPatternChars(), 0, patternChars.length,
+					getR1MinCharCount(),
+					nameChars= getNameChars(name), 0, nameChars.length );
+			return this.tmpRegions.toArray();
+		default:
+			return null;
+		}
+	}
+	
+	private int @Nullable [] getR1PrefixMatches(final char[] pattern, final int patternStart, final int patternEnd,
+			final String name, final int nameStart, final int nameEnd) {
+		if (patternEnd - patternStart == 0) {
+			return null;
+		}
+		if (patternEnd - patternStart == 1) {
+			return new int[] { nameStart, nameStart + 1 };
+		}
+		char patternChar= pattern[patternStart];
+		char nameChar= patternChar; // Character.toLowerCase(name.charAt(nameStart));
+		
+		int patternIdx= patternStart;
+		int nameIdx= nameStart;
+		int matchEnd= nameIdx;
+		while (true) {
+//			if (nameChar == patternChar) {
+//				matchEnd= nameIdx + 1;
+//				if (++patternIdx >= patternEnd) {
+//					return new int[] { nameStart, matchEnd };
+//				}
+//				if (++nameIdx < nameEnd) {
+//					patternChar= pattern[patternIdx];
+//					nameChar= Character.toLowerCase(name.charAt(nameIdx));
+//					continue;
+//				}
+//			}
+//			else if (patternChar == '.' || patternChar == '_') {
+//				if (++patternIdx >= patternEnd) {
+//					return new int[] { nameStart, matchEnd };
+//				}
+//				patternChar= pattern[patternIdx];
+//				continue;
+//			}
+//			else if (nameChar == '.' || nameChar == '_') {
+//				if (++nameIdx < nameEnd) {
+//					nameChar= Character.toLowerCase(name.charAt(nameIdx));
+//					continue;
+//				}
+//			}
+			if (nameChar != patternChar) {
+				if (patternChar == '.' || patternChar == '_') {
+					if (++patternIdx >= patternEnd) {
+						return new int[] { nameStart, matchEnd };
+					}
+					patternChar= pattern[patternIdx];
+					continue;
+				}
+				else if (nameChar == '.' || nameChar == '_') {
+					if (++nameIdx < nameEnd) {
+						nameChar= name.charAt(nameIdx);
+						continue;
+					}
+					throw new IllegalArgumentException();
+				}
+			}
+			matchEnd= nameIdx + 1;
+			if (++patternIdx >= patternEnd) {
+				return new int[] { nameStart, matchEnd };
+			}
+			if (++nameIdx < nameEnd) {
+				patternChar= pattern[patternIdx];
+				nameChar= name.charAt(nameIdx);
+				continue;
+			}
+		}
+	}
+	
+	private void addR1SubstringMatches(final char[] pattern, final int patternStart, final int patternEnd,
+			final int minCharCount,
+			final char[] name, final int nameStart, final int nameEnd) {
+		if (patternEnd - patternStart == 0) {
+			return;
+		}
+		final int nameLastStart= nameEnd - minCharCount;
+		ITER_SUB: for (int nameCurrentStart= nameStart; nameCurrentStart <= nameLastStart; ) {
+			int patternIdx= patternStart;
+			int nameIdx= nameCurrentStart;
+			int matchEnd= nameIdx;
+			
+			char patternChar= pattern[patternIdx];
+			char nameChar= name[nameIdx];
+			if (nameChar == patternChar) {
+				while (true) {
+					if (nameChar == patternChar) {
+						matchEnd= nameIdx + 1;
+						if (++patternIdx >= patternEnd) {
+							this.tmpRegions.add(nameCurrentStart);
+							this.tmpRegions.add(matchEnd);
+							nameCurrentStart= matchEnd;
+							continue ITER_SUB;
+						}
+						if (++nameIdx < nameEnd) {
+							patternChar= pattern[patternIdx];
+							nameChar= name[nameIdx];
+							continue;
+						}
+					}
+					else if (patternChar == '.' || patternChar == '_') {
+						if (++patternIdx >= patternEnd) {
+							this.tmpRegions.add(nameCurrentStart);
+							this.tmpRegions.add(matchEnd);
+							nameCurrentStart= matchEnd;
+							continue ITER_SUB;
+						}
+						patternChar= pattern[patternIdx];
+						continue;
+					}
+					else if (nameChar == '.' || nameChar == '_') {
+						if (++nameIdx < nameEnd) {
+							nameChar= name[nameIdx];
+							continue;
+						}
+					}
+					break;
+				}
+			}
+			nameCurrentStart++;
+			continue ITER_SUB;
+		}
+	}
+	
+}
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/data/RValueFormatter.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/data/RValueFormatter.java
index a8d5906..17c3bc4 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/data/RValueFormatter.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/data/RValueFormatter.java
@@ -174,6 +174,10 @@
 		this.sb.append('}');
 	}
 	
+	private int getEscapedCodepointLength(final int cp) {
+		return 4 + Math.max(((Integer.SIZE - Integer.numberOfLeadingZeros(cp) + 3) / 4), 1);
+	}
+	
 	private void appendEscapedQuoteD(final String value) {
 		int idx= 0;
 		int start= idx;
@@ -937,4 +941,151 @@
 	}
 	
 	
+	/**
+	 * Correct text regions in accordance with {@link #appendName(String, boolean)}
+	 */
+	public void correctNameRegions(final int[] regions,
+			final String name, final boolean quote, final int offset) {
+		if (quote) {
+			correctRegionsEscapeG(name, regions, offset, 1, 1);
+		}
+		else {
+			throw new UnsupportedOperationException();
+		}
+	}
+	
+	private int shiftRegions(final int[] regions, int i, final int endOffset, final int shift) {
+		while (i < regions.length
+				&& regions[i] <= endOffset ) {
+			regions[i++]+= shift;
+		}
+		return i;
+	}
+	
+	private int shiftRegionsClosed(final int[] regions, int i, final int endOffset, final int shift) {
+		while (i < regions.length
+				&& (regions[i] < endOffset || (i % 2 == 1 && regions[i] == endOffset)) ) {
+			regions[i++]+= shift;
+		}
+		return i;
+	}
+	
+	private void shiftRegionsRemaining(final int[] regions, int i, final int shift) {
+		while (i < regions.length) {
+			regions[i++]+= shift;
+		}
+	}
+	
+	/**
+	 * Correct text regions in accordance with {@link #appendEscapedG(String)}
+	 **/
+	private void correctRegionsEscapeG(final String value,
+			final int[] regions, final int valueOffset, int shift, final int shiftAtEnd) {
+		int i= 0;
+		for (;; i++) {
+			if (i >= regions.length) {
+				return;
+			}
+			if (regions[i] < valueOffset) {
+				continue;
+			}
+			else {
+				break;
+			}
+		}
+		
+		int idx= 0;
+		while (idx < value.length()) {
+			final char c= value.charAt(idx);
+			switch (c) {
+			case 0x00:
+			case 0x01:
+			case 0x02:
+			case 0x03:
+			case 0x04:
+			case 0x05:
+			case 0x06:
+			case 0x0E:
+			case 0x0F:
+			case 0x10:
+			case 0x11:
+			case 0x12:
+			case 0x13:
+			case 0x14:
+			case 0x15:
+			case 0x16:
+			case 0x17:
+			case 0x18:
+			case 0x19:
+			case 0x1A:
+			case 0x1B:
+			case 0x1C:
+			case 0x1D:
+			case 0x1E:
+			case 0x1F:
+			case 0x7F:
+				i= shiftRegions(regions, i, valueOffset + idx, shift);
+				if (i >= regions.length) {
+					return;
+				}
+				// appendEscapedCodepoint(c) =>
+				idx++;
+				continue;
+			case 0x07:
+			case 0x08:
+			case 0x09:
+			case 0x0A:
+			case 0x0B:
+			case 0x0C:
+			case 0x0D:
+			case '`':
+			case '\\':
+				i= shiftRegions(regions, i, valueOffset + idx, shift);
+				if (i >= regions.length) {
+					return;
+				}
+				// \\s
+				shift+= 1;
+				idx++;
+				continue;
+			case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27:
+			case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2E: case 0x2F:
+			case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37:
+			case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3E: case 0x3F:
+			case 0x40: case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x46: case 0x47:
+			case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4E: case 0x4F:
+			case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
+			case 0x58: case 0x59: case 0x5A: case 0x5B: /* \\   */ case 0x5D: case 0x5E: case 0x5F:
+			/* `    */ case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67:
+			case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6D: case 0x6E: case 0x6F:
+			case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
+			case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: /* DEL  */
+				idx++;
+				continue;
+			default:
+				{	i= shiftRegions(regions, i, valueOffset + idx, shift);
+					if (i >= regions.length) {
+						return;
+					}
+					final int cp= value.codePointAt(idx);
+					if (cp != c) {
+						if (!isPrintableChar(cp)) {
+							shift+= getEscapedCodepointLength(cp) - 2;
+						}
+						idx+= 2;
+					}
+					else {
+						if (!isPrintableChar(cp)) {
+							shift+= getEscapedCodepointLength(cp) - 1;
+						}
+						idx++;
+					}
+					continue;
+				}
+			}
+		}
+		i= shiftRegionsClosed(regions, i, valueOffset + idx, shift); // idx == value.length()
+		shiftRegionsRemaining(regions, i, shift + shiftAtEnd);
+	}
+	
 }
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRFrame.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRFrame.java
index f5250f7..e595dcb 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRFrame.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRFrame.java
@@ -16,6 +16,8 @@
 
 import java.util.List;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ltk.model.core.elements.IModelElement;
 
 
@@ -43,7 +45,7 @@
 	
 	List<? extends IRElement> getModelElements();
 	boolean hasModelChildren(final IModelElement.Filter filter);
-	List<? extends IRLangElement> getModelChildren(final IModelElement.Filter filter);
+	List<? extends IRLangElement> getModelChildren(final IModelElement. @Nullable Filter filter);
 	List<? extends IRFrame> getPotentialParents();
 	
 }
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRLangElement.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRLangElement.java
index 0962730..7d4ccde 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRLangElement.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRLangElement.java
@@ -16,16 +16,19 @@
 
 import java.util.List;
 
-import org.eclipse.statet.ltk.model.core.elements.IModelElement;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ltk.model.core.elements.ISourceElement;
 
 
+@NonNullByDefault
 public interface IRLangElement extends IRElement, ISourceElement {
 	
 	
 	@Override
-	boolean hasModelChildren(IModelElement.Filter filter);
+	boolean hasModelChildren(@Nullable Filter filter);
 	@Override
-	List<? extends IRLangElement> getModelChildren(IModelElement.Filter filter);
+	List<? extends IRLangElement> getModelChildren(@Nullable Filter filter);
 	
 }
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRMethod.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRMethod.java
index cb4063d..2db5f07 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRMethod.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/IRMethod.java
@@ -14,6 +14,7 @@
 
 package org.eclipse.statet.r.core.model;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 
 /**
@@ -27,6 +28,6 @@
 	 * 
 	 * @return the argument definition or <code>null</code> if unknown
 	 */
-	ArgsDefinition getArgsDefinition();
+	@Nullable ArgsDefinition getArgsDefinition();
 	
 }
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/RElementName.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/RElementName.java
index f062601..928965e 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/RElementName.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/model/RElementName.java
@@ -940,6 +940,15 @@
 		return createDisplayName(this, options);
 	}
 	
+	@Override
+	public int[] correctDisplayNameRegions(final int[] regions, final int offset) {
+		final String segmentName= getSegmentName();
+		if (segmentName != null && !isValidSymbol(segmentName)) {
+			new RValueFormatter().correctNameRegions(regions, segmentName, true, offset);
+		}
+		return regions;
+	}
+	
 	
 	protected boolean isDefaultImpl() {
 		return (getClass() == DefaultImpl.class);
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTerminal.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTerminal.java
index 901e057..6485beb 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTerminal.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTerminal.java
@@ -14,7 +14,11 @@
 
 package org.eclipse.statet.r.core.rlang;
 
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 
+
+@NonNullByDefault
 public enum RTerminal {
 	
 	EOF (""), //$NON-NLS-1$
@@ -171,7 +175,7 @@
 	
 	
 	public static String[] textArray(final RTerminal[] list) {
-		final String[] texts= new String[list.length];
+		final String[] texts= new @NonNull String[list.length];
 		for (int i= 0; i < texts.length; i++) {
 			texts[i]= list[i].text;
 		}
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTokens.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTokens.java
index 2600cfa..728b051 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTokens.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rlang/RTokens.java
@@ -16,12 +16,15 @@
 
 import java.util.Arrays;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+
 
 /**
  * Provides util-method for tokens of the R-language.
  * 'Tokes' means tokens according to R-language definition, they defines not
  * directly <code>Token</code>, implementions of <code>IToken</code>.
  */
+@NonNullByDefault
 public final class RTokens {
 	
 	public static final String[] CONSTANT_WORDS= new String[] {
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/FCall.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/FCall.java
index 4937cda..eb7c905 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/FCall.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/FCall.java
@@ -22,6 +22,9 @@
 
 import org.eclipse.statet.jcommons.collections.IntArrayList;
 import org.eclipse.statet.jcommons.collections.IntList;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ltk.ast.core.AstNode;
 import org.eclipse.statet.ltk.ast.core.AstVisitor;
@@ -31,6 +34,7 @@
 /**
  * <code>§ref§ ( §args§ )</code>
  */
+@NonNullByDefault
 public final class FCall extends RAstNode {
 	
 	
@@ -59,7 +63,7 @@
 		}
 		
 		@Override
-		public final RTerminal getOperator(final int index) {
+		public final @Nullable RTerminal getOperator(final int index) {
 			return null;
 		}
 		
@@ -90,7 +94,7 @@
 		
 		@Override
 		public final FCall.Arg[] getChildren() {
-			return this.specs.toArray(new FCall.Arg[this.specs.size()]);
+			return this.specs.toArray(new FCall. @NonNull Arg[this.specs.size()]);
 		}
 		
 		@Override
@@ -117,17 +121,17 @@
 		
 		
 		@Override
-		final Expression getExpr(final RAstNode child) {
+		final @Nullable Expression getExpr(final RAstNode child) {
 			return null;
 		}
 		
 		@Override
-		final Expression getLeftExpr() {
+		final @Nullable Expression getLeftExpr() {
 			return null;
 		}
 		
 		@Override
-		final Expression getRightExpr() {
+		final @Nullable Expression getRightExpr() {
 			return null;
 		}
 		
@@ -191,7 +195,7 @@
 	}
 	
 	@Override
-	public final RTerminal getOperator(final int index) {
+	public final @Nullable RTerminal getOperator(final int index) {
 		return null;
 	}
 	
@@ -269,7 +273,7 @@
 	
 	
 	@Override
-	final Expression getExpr(final RAstNode child) {
+	final @Nullable Expression getExpr(final RAstNode child) {
 		if (this.refExpr.node == child) {
 			return this.refExpr;
 		}
@@ -282,7 +286,7 @@
 	}
 	
 	@Override
-	final Expression getRightExpr() {
+	final @Nullable Expression getRightExpr() {
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
index dfd5bd6..cb5ede1 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAst.java
@@ -26,6 +26,9 @@
 
 import org.eclipse.statet.jcommons.collections.IntArrayList;
 import org.eclipse.statet.jcommons.collections.IntList;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.text.core.BasicTextRegion;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
 
@@ -348,11 +351,12 @@
 		return null;
 	}
 	
+	@NonNullByDefault
 	public static final class FCallArgMatch {
 		
 		public final ArgsDefinition argsDef;
 		public final FCall.Args argsNode;
-		public final FCall.Arg[] allocatedArgs;
+		public final FCall. @Nullable Arg[] allocatedArgs;
 		public final FCall.Arg[] ellipsisArgs;
 		public final FCall.Arg[] otherArgs;
 		public final int[] argsNode2argsDef;
@@ -361,7 +365,7 @@
 		private FCallArgMatch(
 				final ArgsDefinition argsDef, 
 				final FCall.Args argsNode, 
-				final FCall.Arg[] allocatedArgs, 
+				final FCall. @Nullable Arg[] allocatedArgs, 
 				final FCall.Arg[] ellipsisArgs, 
 				final FCall.Arg[] otherArgs,
 				final int[] argsNode2argsDef) {
@@ -374,7 +378,7 @@
 		}
 		
 		
-		public FCall.Arg getArgNode(final String name) {
+		public FCall. @Nullable Arg getArgNode(final String name) {
 			final int idx= this.argsDef.indexOf(name);
 			if (idx >= 0) {
 				return this.allocatedArgs[idx];
@@ -384,7 +388,7 @@
 			}
 		}
 		
-		public FCall.Arg getArgNode(final int callArgIdx) {
+		public FCall. @Nullable Arg getArgNode(final int callArgIdx) {
 			if (callArgIdx >= 0) {
 				return this.allocatedArgs[callArgIdx];
 			}
@@ -393,7 +397,7 @@
 			}
 		}
 		
-		public RAstNode getArgValueNode(final String name) {
+		public @Nullable RAstNode getArgValueNode(final String name) {
 			final int idx= this.argsDef.indexOf(name);
 			if (idx >= 0 && this.allocatedArgs[idx] != null) {
 				return this.allocatedArgs[idx].getValueChild();
@@ -403,7 +407,7 @@
 			}
 		}
 		
-		public RAstNode getArgValueNode(final int callArgIdx) {
+		public @Nullable RAstNode getArgValueNode(final int callArgIdx) {
 			if (callArgIdx >= 0 && this.allocatedArgs[callArgIdx] != null) {
 				return this.allocatedArgs[callArgIdx].getValueChild();
 			}
@@ -412,7 +416,7 @@
 			}
 		}
 		
-		public ArgsDefinition.Arg getArgDef(final int callArgIdx) {
+		public ArgsDefinition. @Nullable Arg getArgDef(final int callArgIdx) {
 			if (callArgIdx >= 0 && this.argsDef.size() > 0) {
 				if (callArgIdx < this.argsNode2argsDef.length) {
 					if (this.argsNode2argsDef[callArgIdx] >= 0) {
@@ -434,7 +438,7 @@
 		
 	}
 	
-	public static final FCall.Arg[] NO_ARGS= new FCall.Arg[0];
+	public static final FCall.Arg @NonNull [] NO_ARGS= new FCall.Arg[0];
 	
 	private static final int FAIL= -1;
 	private static final int ELLIPSIS= -2;
@@ -473,10 +477,12 @@
 	 * @param argsDef the arguments definition
 	 * @return
 	 */
+	@NonNullByDefault
 	public static FCallArgMatch matchArgs(final FCall.Args argsNode, final ArgsDefinition argsDef) {
 		final int nodeArgsCount= argsNode.getChildCount();
 		final int defArgsCount= argsDef.size();
-		final FCall.Arg[] allocatedArgs= (defArgsCount > 0) ? new FCall.Arg[defArgsCount] : NO_ARGS;
+		final FCall. @Nullable Arg[] allocatedArgs= (defArgsCount > 0) ?
+				new FCall. @Nullable Arg[defArgsCount] : NO_ARGS;
 		final int ellipsisDefIdx= argsDef.indexOf("..."); //$NON-NLS-1$
 		
 		final int[] match= new int[nodeArgsCount];
@@ -513,7 +519,7 @@
 		final int ellipsisType= (ellipsisDefIdx >= 0) ? ELLIPSIS : FAIL;
 		final int testStop= (ellipsisDefIdx >= 0) ? ellipsisDefIdx : defArgsCount;
 		if (testPartial) {
-			FCall.Arg[] partialArgs= null;
+			FCall. @Nullable Arg[] partialArgs= null;
 			ITER_ARGS: for (int nodeIdx= 0; nodeIdx < nodeArgsCount; nodeIdx++) {
 				if (match[nodeIdx] == TEST_PARTIAL) {
 					final FCall.Arg argNode= argsNode.getChild(nodeIdx);
@@ -533,7 +539,7 @@
 					}
 					if (matchIdx >= 0) {
 						if (partialArgs == null) {
-							partialArgs= new FCall.Arg[testStop];
+							partialArgs= new FCall. @Nullable Arg[testStop];
 							partialArgs[matchIdx]= argNode;
 							match[nodeIdx]= matchIdx;
 							continue ITER_ARGS;
@@ -584,8 +590,8 @@
 			failCount+= ellipsisCount;
 			ellipsisCount= 0;
 		}
-		final FCall.Arg[] ellipsisArgs= (ellipsisCount > 0) ? new FCall.Arg[ellipsisCount] : NO_ARGS;
-		final FCall.Arg[] otherArgs= (failCount > 0) ? new FCall.Arg[failCount] : NO_ARGS;
+		final FCall.Arg[] ellipsisArgs= (ellipsisCount > 0) ? new FCall. @NonNull Arg[ellipsisCount] : NO_ARGS;
+		final FCall.Arg[] otherArgs= (failCount > 0) ? new FCall. @NonNull Arg[failCount] : NO_ARGS;
 		if (ellipsisCount > 0 || failCount > 0) {
 			int ellipsisIdx= 0;
 			int otherIdx= 0;
diff --git a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAstNode.java b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAstNode.java
index 0317b21..27a5e27 100644
--- a/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAstNode.java
+++ b/r/org.eclipse.statet.r.core/src/org/eclipse/statet/r/core/rsource/ast/RAstNode.java
@@ -19,6 +19,8 @@
 import java.lang.reflect.InvocationTargetException;
 import java.util.List;
 
+import org.eclipse.statet.jcommons.lang.Nullable;
+
 import org.eclipse.statet.ltk.ast.core.AstNode;
 import org.eclipse.statet.ltk.ast.core.impl.AbstractAstNode;
 import org.eclipse.statet.r.core.rlang.RTerminal;
@@ -60,7 +62,7 @@
 	
 	public abstract NodeType getNodeType();
 	
-	public abstract RTerminal getOperator(final int index);
+	public abstract @Nullable RTerminal getOperator(final int index);
 	
 	@Override
 	public final int getStatusCode() {
@@ -148,9 +150,9 @@
 	}
 	
 	
-	abstract Expression getExpr(RAstNode child);
-	abstract Expression getLeftExpr();
-	abstract Expression getRightExpr();
+	abstract @Nullable Expression getExpr(RAstNode child);
+	abstract @Nullable Expression getLeftExpr();
+	abstract @Nullable Expression getRightExpr();
 	
 	public final boolean equalsIgnoreAst(final Object obj) {
 		if (this == obj) {
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
index 648c5d3..8b266ea 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RArgumentListContextInformation.java
@@ -20,13 +20,13 @@
 import org.eclipse.statet.jcommons.collections.IntArrayList;
 import org.eclipse.statet.jcommons.collections.IntList;
 
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IAssistInformationProposal;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInformationProposal;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
 import org.eclipse.statet.r.core.model.IRMethod;
 import org.eclipse.statet.r.ui.RLabelProvider;
 
 
-public class RArgumentListContextInformation implements IAssistInformationProposal,
+public class RArgumentListContextInformation implements AssistInformationProposal,
 		IContextInformationExtension {
 	
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/REditorTemplateCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/REditorTemplateCompletionComputer.java
index 88246ad..c47d1f7 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/REditorTemplateCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/REditorTemplateCompletionComputer.java
@@ -46,15 +46,15 @@
 	
 	
 	@Override
-	protected String extractPrefix(final AssistInvocationContext context) {
+	protected @Nullable String extractPrefix(final AssistInvocationContext context) {
 		try {
-			final IDocument document = context.getSourceViewer().getDocument();
-			final int end = context.getInvocationOffset();
-			final int start = Math.max(end-50, 0);
-			final String text = document.get(start, end-start);
-			int i = text.length()-1;
+			final IDocument document= context.getSourceViewer().getDocument();
+			final int end= context.getInvocationOffset();
+			final int start= Math.max(end - 50, 0);
+			final String text= document.get(start, end-start);
+			int i= text.length() - 1;
 			while (i >= 0) {
-				final char c = text.charAt(i);
+				final char c= text.charAt(i);
 				if (Character.isLetterOrDigit(c) || c == '.' || c == '_') {
 					i--;
 					continue;
@@ -62,19 +62,21 @@
 				if (c == '\\' || c == '@') {
 					return text.substring(i);
 				}
-				return text.substring(i+1);
+				return text.substring(i + 1);
 			}
+			return ""; //$NON-NLS-1$
 		}
-		catch (final BadLocationException e) {}
-		return ""; //$NON-NLS-1$
+		catch (final BadLocationException e) {
+			return null;
+		}
 	}
 	
 	@Override
 	protected @Nullable TemplateContextType getContextType(
 			final AssistInvocationContext context, final TextRegion region) {
 		try {
-			final ISourceEditor editor = context.getEditor();
-			final AbstractDocument document = (AbstractDocument) context.getSourceViewer().getDocument();
+			final ISourceEditor editor= context.getEditor();
+			final AbstractDocument document= (AbstractDocument) context.getSourceViewer().getDocument();
 			final ITypedRegion partition= document.getPartition(
 					editor.getDocumentContentInfo().getPartitioning(), region.getStartOffset(), true );
 			if (partition.getType() == IRDocumentConstants.R_ROXYGEN_CONTENT_TYPE) {
@@ -84,8 +86,7 @@
 				return getTypeRegistry().getContextType(REditorTemplateContextType.RCODE_CONTEXTTYPE_ID);
 			}
 		}
-		catch (final BadPartitioningException e) {}
-		catch (final BadLocationException e) {}
+		catch (final BadPartitioningException | BadLocationException e) {}
 		return null;
 	}
 	
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RElementCompletionProposal.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RElementCompletionProposal.java
index db5e011..49feacb 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RElementCompletionProposal.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RElementCompletionProposal.java
@@ -23,22 +23,24 @@
 import org.eclipse.jface.text.IInformationControlCreator;
 import org.eclipse.jface.text.IRegion;
 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
-import org.eclipse.jface.text.contentassist.IContextInformation;
 import org.eclipse.jface.text.link.LinkedModeModel;
 import org.eclipse.jface.text.link.LinkedModeUI;
 import org.eclipse.jface.text.link.LinkedPositionGroup;
-import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.widgets.Shell;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.text.core.SearchPattern;
+
 import org.eclipse.statet.ecommons.text.ui.BracketLevel.InBracketPosition;
 
 import org.eclipse.statet.internal.r.ui.rhelp.RHelpInfoHoverCreator;
 import org.eclipse.statet.internal.r.ui.rhelp.RHelpLtkUI;
 import org.eclipse.statet.ltk.core.ElementName;
-import org.eclipse.statet.ltk.ui.IElementLabelProvider;
+import org.eclipse.statet.ltk.ui.ElementLabelProvider;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ElementNameCompletionProposal;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.InfoHover;
@@ -46,7 +48,6 @@
 import org.eclipse.statet.r.core.IRCoreAccess;
 import org.eclipse.statet.r.core.RCodeStyleSettings;
 import org.eclipse.statet.r.core.RCore;
-import org.eclipse.statet.r.core.RSymbolComparator;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
 import org.eclipse.statet.r.core.model.IRElement;
 import org.eclipse.statet.r.core.model.IRMethod;
@@ -58,88 +59,91 @@
 import org.eclipse.statet.rhelp.core.REnvHelp;
 import org.eclipse.statet.rhelp.core.RHelpManager;
 import org.eclipse.statet.rhelp.core.RPkgHelp;
-import org.eclipse.statet.rj.renv.core.RPkgBuilt;
 
 
-public class RElementCompletionProposal extends ElementNameCompletionProposal<IRElement>
+@NonNullByDefault
+public class RElementCompletionProposal
+		extends ElementNameCompletionProposal<RAssistInvocationContext, IRElement>
 		implements ICompletionProposalExtension5 {
 	
 	
-	private static final int PACKAGE_NAME= 1;
-	private static final int ARGUMENT_NAME= 2;
-	private static final int FUNCTION= 3;
+	public static class RElementProposalParameters extends ProposalParameters<RAssistInvocationContext> {
+		
+		
+		public final ElementLabelProvider labelProvider;
+		
+		
+		public ElementName replacementName;
+		
+		public IRElement element;
+		
+		
+		public RElementProposalParameters(final RAssistInvocationContext context, final int replacementOffset,
+				final SearchPattern namePattern, final int baseRelevance,
+				final ElementLabelProvider labelProvider) {
+			super(context, replacementOffset, namePattern, baseRelevance);
+			
+			this.labelProvider= labelProvider;
+		}
+		
+		public RElementProposalParameters(final RAssistInvocationContext context, final int replacementOffset,
+				final SearchPattern namePattern,
+				final ElementLabelProvider labelProvider) {
+			this(context, replacementOffset, namePattern, 0, labelProvider);
+		}
+		
+	}
+	
+	
+	protected static final int PACKAGE_NAME= 1;
+	protected static final int ARGUMENT_NAME= 2;
+	protected static final int FUNCTION= 3;
+	
+	
+	private static boolean rHelpInfoHoverInitialized;
+	private static @Nullable IInformationControlCreator rHelpInfoHoverCreator;
+	
+	private static @Nullable IInformationControlCreator getRHelpInfoHoverCreator(final AssistInvocationContext context) {
+		if (!rHelpInfoHoverInitialized) {
+			final Shell shell= context.getSourceViewer().getTextWidget().getShell();
+			if (shell != null) {
+				if (RHelpInfoHoverCreator.isAvailable(shell)) {
+					rHelpInfoHoverCreator= new RHelpInfoHoverCreator(InfoHover.MODE_PROPOSAL_INFO);
+				}
+				rHelpInfoHoverInitialized= true;
+			}
+		}
+		return rHelpInfoHoverCreator;
+	}
 	
 	
 	public static class ArgumentProposal extends RElementCompletionProposal {
 		
 		
-		public ArgumentProposal(final RAssistInvocationContext context, 
-				final ElementName replacementName, final int replacementOffset,
-				final IRElement element, final int relevance, final IElementLabelProvider labelProvider) {
-			super(context, replacementName, replacementOffset, element, relevance, labelProvider);
+		public ArgumentProposal(final RElementProposalParameters parameters) {
+			super(parameters);
 		}
 		
 		
 		@Override
-		public Image getImage() {
-			return RUI.getImage(RUI.IMG_OBJ_ARGUMENT_ASSIGN);
+		protected int getMode() {
+			return ARGUMENT_NAME;
 		}
 		
+		
 		@Override
 		public String getDisplayString() {
 			return getReplacementName().getDisplayName();
 		}
 		
 		@Override
-		public StyledString getStyledDisplayString() {
+		public StyledString computeStyledText() {
 			return new StyledString(getReplacementName().getDisplayName());
 		}
 		
 		@Override
-		protected int getMode() {
-			return ARGUMENT_NAME;
-		}
-		
-	}
-	
-	public static class RPkgProposal extends RElementCompletionProposal {
-		
-		
-		private final RPkgBuilt pkgInfo;
-		
-		
-		public RPkgProposal(final RAssistInvocationContext context, 
-				final ElementName replacementName, final int replacementOffset,
-				final RPkgBuilt pkgInfo, final int relevance) {
-			super(context, replacementName, replacementOffset, null, relevance, null);
-			
-			this.pkgInfo= pkgInfo;
-		}
-		
-		
-		@Override
 		public Image getImage() {
-			return RUI.getImage(RUI.IMG_OBJ_R_PACKAGE);
-		}
-		
-		@Override
-		public String getDisplayString() {
-			return getReplacementName().getSegmentName();
-		}
-		
-		@Override
-		public StyledString getStyledDisplayString() {
-			final StyledString s= new StyledString(getReplacementName().getSegmentName());
-			if (this.pkgInfo != null) {
-				s.append(QUALIFIER_SEPARATOR, StyledString.QUALIFIER_STYLER);
-				s.append(this.pkgInfo.getTitle(), StyledString.QUALIFIER_STYLER);
-			}
-			return s;
-		}
-		
-		@Override
-		protected int getMode() {
-			return PACKAGE_NAME;
+			return RUI.getImage(RUI.IMG_OBJ_ARGUMENT_ASSIGN);
 		}
 		
 	}
@@ -147,11 +151,8 @@
 	public static class ContextInformationProposal extends RElementCompletionProposal {
 		
 		
-		public ContextInformationProposal(final RAssistInvocationContext context,
-				final ElementName elementName, final int replacementOffset,
-				final IRElement element, final int relevance,
-				final IElementLabelProvider labelProvider) {
-			super(context, elementName, replacementOffset, element, relevance, labelProvider);
+		public ContextInformationProposal(final RElementProposalParameters parameters) {
+			super(parameters);
 		}
 		
 		
@@ -169,129 +170,79 @@
 		protected void doApply(final char trigger, final int stateMask,
 				final int caretOffset, final int replacementOffset, final int replacementLength)
 				throws BadLocationException {
-			final ApplyData data= getApplyData();
-			setCursorPosition(-1);
-			data.setContextInformation(new RArgumentListContextInformation(replacementOffset,
+			final ApplyData applyData= getApplyData();
+			
+			applyData.clearSelection();
+			applyData.setContextInformation(new RArgumentListContextInformation(replacementOffset,
 					(IRMethod) getElement() ));
 		}
 		
 	}
 	
 	
-	static final class ApplyData {
-		
-		private final RAssistInvocationContext context;
-		private final SourceViewer viewer;
-		private final IDocument document;
-		
-		private IContextInformation contextInformation;
-		
-		ApplyData(final RAssistInvocationContext context) {
-			this.context= context;
-			this.viewer= context.getSourceViewer();
-			this.document= this.viewer.getDocument();
-		}
-		
-		public SourceViewer getViewer() {
-			return this.viewer;
-		}
-		
-		public IDocument getDocument() {
-			return this.document;
-		}
-		
-		public RHeuristicTokenScanner getScanner() {
-			return this.context.getRHeuristicTokenScanner();
-		}
-		
-		public void setContextInformation(final IContextInformation info) {
-			this.contextInformation= info;
-		}
-		
-		public IContextInformation getContextInformation() {
-			return this.contextInformation;
-		}
-		
-	}
-	
-	
-	private static final boolean isFollowedByOpeningBracket(final ApplyData util, final int forwardOffset) {
-		final RHeuristicTokenScanner scanner= util.getScanner();
-		scanner.configure(util.getDocument());
+	private static final boolean isFollowedByOpeningBracket(final int forwardOffset,
+			final RAssistInvocationContext context) {
+		final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
+		scanner.configure(context.getDocument());
 		final int idx= scanner.findAnyNonBlankForward(forwardOffset, RHeuristicTokenScanner.UNBOUND, false);
 		return (idx >= 0
 				&& scanner.getChar() == '(' );
 	}
 	
-	private static final boolean isClosedBracket(final ApplyData data, final int backwardOffset, final int forwardOffset) {
+	private static final boolean isClosedBracket(final int backwardOffset, final int forwardOffset,
+			final RAssistInvocationContext context) {
 		final int searchType= RHeuristicTokenScanner.ROUND_BRACKET_TYPE;
 		int[] balance= new int[3];
 		balance[searchType]++;
-		final RHeuristicTokenScanner scanner= data.getScanner();
-		scanner.configureDefaultParitions(data.getDocument());
+		final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
+		scanner.configureDefaultParitions(context.getDocument());
 		balance= scanner.computeBracketBalance(backwardOffset, forwardOffset, balance, searchType);
 		return (balance[searchType] <= 0);
 	}
 	
-	private static final boolean isFollowedByEqualAssign(final ApplyData data, final int forwardOffset) {
-		final RHeuristicTokenScanner scanner= data.getScanner();
-		scanner.configure(data.getDocument());
+	private static final boolean isFollowedByEqualAssign(final int forwardOffset,
+			final RAssistInvocationContext context) {
+		final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
+		scanner.configure(context.getDocument());
 		final int idx= scanner.findAnyNonBlankForward(forwardOffset, RHeuristicTokenScanner.UNBOUND, false);
 		return (idx >= 0
 				&& scanner.getChar() == '=' );
 	}
 	
-	private static final boolean isFollowedByAssign(final ApplyData util, final int forwardOffset) {
-		final RHeuristicTokenScanner scanner= util.getScanner();
-		scanner.configure(util.getDocument());
+	private static final boolean isFollowedByAssign(final int forwardOffset,
+			final RAssistInvocationContext data) {
+		final RHeuristicTokenScanner scanner= data.getRHeuristicTokenScanner();
+		scanner.configure(data.getDocument());
 		final int idx= scanner.findAnyNonBlankForward(forwardOffset, RHeuristicTokenScanner.UNBOUND, false);
 		return (idx >= 0
 				&& (scanner.getChar() == '=' || scanner.getChar() == '<') );
 	}
 	
 	
-	private ApplyData applyData;
-	
-	private IInformationControlCreator informationControlCreator;
-	
-	
-	public RElementCompletionProposal(final RAssistInvocationContext context, final ElementName elementName, 
-			final int replacementOffset, final IRElement element,
-			final int relevance, final IElementLabelProvider labelProvider) {
-		super(context, elementName, replacementOffset, element, relevance, labelProvider);
+	public RElementCompletionProposal(final RElementProposalParameters parameters) {
+		super(parameters, parameters.replacementName, parameters.element, parameters.labelProvider);
 	}
 	
 	
-	@Override
-	protected String getPluginId() {
-		return RUI.BUNDLE_ID;
-	}
-	
-	@Override
-	public RAssistInvocationContext getInvocationContext() {
-		return (RAssistInvocationContext) super.getInvocationContext();
-	}
-	
-	protected final ApplyData getApplyData() {
-		if (this.applyData == null) {
-			this.applyData= new ApplyData(getInvocationContext());
-		}
-		return this.applyData;
-	}
-	
 	protected IRCoreAccess getRCoreAccess() {
 		return getInvocationContext().getEditor().getRCoreAccess();
 	}
 	
+	protected int getMode() {
+		return (getElement() != null
+						&& (getElement().getElementType() & IRElement.MASK_C1) == IRElement.C1_METHOD) ?
+				FUNCTION : 0;
+	}
+	
 	
 	@Override
 	protected int computeReplacementLength(final int replacementOffset, final Point selection, final int caretOffset, final boolean overwrite) {
 		// keep in synch with RSimpleCompletionProposal
 		final int end= Math.max(caretOffset, selection.x + selection.y);
 		if (overwrite) {
-			final ApplyData data= getApplyData();
-			final RHeuristicTokenScanner scanner= data.getScanner();
-			scanner.configure(data.getDocument());
+			final RAssistInvocationContext context= getInvocationContext();
+			final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
+			scanner.configure(context.getDocument());
 			final IRegion word= scanner.findRWord(end, false, true);
 			if (word != null) {
 				return (word.getOffset() + word.getLength() - replacementOffset);
@@ -300,36 +251,38 @@
 		return (end - replacementOffset);
 	}
 	
+	
 	@Override
-	public boolean validate(final IDocument document, final int offset, final DocumentEvent event) {
+	protected @Nullable String getValidationPrefix(final int offset) throws BadLocationException {
 		// keep in synch with RSimpleCompletionProposal
-		try {
-			int start= getReplacementOffset();
-			int length= offset - start;
-			if (length > 0 && document.getChar(start) == '`') {
-				start++;
-				length--;
+		final int startOffset= getReplacementOffset();
+		if (offset >= startOffset) {
+			final RAssistInvocationContext context= getInvocationContext();
+			final IDocument document= context.getDocument();
+			int nameStartOffset= startOffset;
+			int nameEndOffset= offset;
+			if (nameEndOffset > nameStartOffset && document.getChar(nameStartOffset) == '`') {
+				nameStartOffset++;
 			}
-			if (length > 0 && document.getChar(start+length-1) == '`') {
-				length--;
+			if (nameEndOffset > nameStartOffset && document.getChar(nameEndOffset - 1) == '`') {
+				nameEndOffset--;
 			}
-			final String prefix= document.get(start, length);
-			final String replacement= getReplacementName().getSegmentName();
-			if (new RSymbolComparator.PrefixPattern(prefix).matches(replacement)) {
-				return true;
+			if (nameEndOffset >= nameStartOffset) {
+				return context.getIdentifierSegmentName(
+						document.get(startOffset, offset - startOffset) );
 			}
 		}
-		catch (final BadLocationException e) {
-			// ignore concurrently modified document
-		}
-		return false;
+		return null;
 	}
 	
+	
 	@Override
 	protected void doApply(final char trigger, final int stateMask, final int caretOffset,
-			final int replacementOffset, int replacementLength) throws BadLocationException {
-		final ApplyData data= getApplyData();
-		final IDocument document= data.getDocument();
+			final int replacementOffset, int replacementLength)
+			throws BadLocationException {
+		final RAssistInvocationContext context= getInvocationContext();
+		final IDocument document= context.getDocument();
+		final ApplyData applyData= getApplyData();
 		
 		final ElementName replacementName= getReplacementName();
 		final int mode= getMode();
@@ -339,7 +292,7 @@
 		final ElementName elementName;
 		if (assignmentFunction) {
 			elementName= RElementName.create(RElementName.MAIN_DEFAULT,
-					replacementName.getSegmentName().substring(0, replacementName.getSegmentName().length()-2) );
+					replacementName.getSegmentName().substring(0, replacementName.getSegmentName().length() - 2) );
 		}
 		else {
 			elementName= replacementName;
@@ -356,7 +309,7 @@
 			}
 			replacement.insert(elementName.getSegmentName().length(), '`');
 			replacement.insert(0, '`');
-			cursor += 2;
+			cursor+= 2;
 		}
 		
 		int subMode= 0;
@@ -372,28 +325,28 @@
 				cursor ++;
 				subMode= 10;
 			}
-			else if (!isFollowedByOpeningBracket(data, replacementOffset+replacementLength)) {
+			else if (!isFollowedByOpeningBracket(replacementOffset + replacementLength, context)) {
 				replacement.append('(');
 				cursor ++;
 				subMode= 11;
 			}
 			if (subMode >= 10) {
 				if (subMode == 11
-						&& !isClosedBracket(data, replacementOffset, replacementOffset+replacementLength)) {
+						&& !isClosedBracket(replacementOffset, replacementOffset + replacementLength, context)) {
 					replacement.append(')');
 					linkedMode= 2;
 					
-					if (assignmentFunction && !isFollowedByAssign(data, replacementOffset+replacementLength)) {
+					if (assignmentFunction && !isFollowedByAssign(replacementOffset + replacementLength, context)) {
 						replacement.append(" <- "); //$NON-NLS-1$
 						if (linkedMode >= 0) {
-							linkedMode += 4;
+							linkedMode+= 4;
 						}
 					}
 				}
 				
 				final ArgsDefinition argsDef= rMethod.getArgsDefinition();
 				if (argsDef == null || argsDef.size() > 0 || (subMode == 11 && linkedMode < 0)) {
-					data.setContextInformation(new RArgumentListContextInformation(replacementOffset + cursor, rMethod));
+					applyData.setContextInformation(new RArgumentListContextInformation(replacementOffset + cursor, rMethod));
 				}
 				else {
 					cursor ++;
@@ -403,32 +356,33 @@
 			break;
 		
 		case ARGUMENT_NAME:
-			if (!isFollowedByEqualAssign(data, replacementOffset+replacementLength)) {
+			if (!isFollowedByEqualAssign(replacementOffset+replacementLength, context)) {
 				final RCodeStyleSettings codeStyle= getRCoreAccess().getRCodeStyle();
 				final String argAssign= codeStyle.getArgAssignString();
 				replacement.append(argAssign);
-				cursor += argAssign.length();
+				cursor+= argAssign.length();
 			}
 			break;
 		
 		}
 		
 		document.replace(replacementOffset, replacementLength, replacement.toString());
-		setCursorPosition(replacementOffset + cursor);
+		applyData.setSelection(replacementOffset + cursor);
 		if (linkedMode >= 0) {
-			createLinkedMode(data, replacementOffset + cursor - 1, linkedMode).enter();
+			createLinkedMode(replacementOffset + cursor - 1, linkedMode).enter();
 		}
 	}
 	
-	private LinkedModeUI createLinkedMode(final ApplyData util, final int offset, final int mode)
+	private LinkedModeUI createLinkedMode(final int offset, final int mode)
 			throws BadLocationException {
 		final AssistInvocationContext context= getInvocationContext();
+		final IDocument document= context.getDocument();
 		
 		final LinkedModeModel model= new LinkedModeModel();
 		int pos= 0;
 		
 		final LinkedPositionGroup group= new LinkedPositionGroup();
-		final InBracketPosition position= RBracketLevel.createPosition('(', util.getDocument(),
+		final InBracketPosition position= RBracketLevel.createPosition('(', document,
 				offset + 1, 0, pos++);
 		group.addPosition(position);
 		model.addGroup(group);
@@ -436,46 +390,26 @@
 		model.forceInstall();
 		
 		final RBracketLevel level= new RBracketLevel(model,
-				util.getDocument(), context.getEditor().getDocumentContentInfo(),
-				position, (util.getViewer() instanceof InputSourceViewer), true);
+				document, context.getEditor().getDocumentContentInfo(),
+				position, (context.getSourceViewer() instanceof InputSourceViewer), true);
 		
 		/* create UI */
-		final LinkedModeUI ui= new LinkedModeUI(model, util.getViewer());
+		final LinkedModeUI ui= new LinkedModeUI(model, context.getSourceViewer());
 		ui.setCyclingMode(LinkedModeUI.CYCLE_NEVER);
-		ui.setExitPosition(util.getViewer(), offset + (mode & 0xff), 0, pos);
+		ui.setExitPosition(context.getSourceViewer(), offset + (mode & 0xff), 0, pos);
 		ui.setSimpleMode(true);
 		ui.setExitPolicy(level);
 		return ui;
 	}
 	
-	protected int getMode() {
-		return (getElement() != null
-						&& (getElement().getElementType() & IRElement.MASK_C1) == IRElement.C1_METHOD) ?
-				FUNCTION : 0;
-	}
-	
-	
-	@Override
-	public IContextInformation getContextInformation() {
-		return getApplyData().getContextInformation();
-	}
-	
 	
 	@Override
 	public IInformationControlCreator getInformationControlCreator() {
-		final Shell shell= getInvocationContext().getSourceViewer().getTextWidget().getShell();
-		if (shell == null || !RHelpInfoHoverCreator.isAvailable(shell)) {
-			return null;
-		}
-		
-		if (this.informationControlCreator == null) {
-			this.informationControlCreator= new RHelpInfoHoverCreator(InfoHover.MODE_PROPOSAL_INFO);
-		}
-		return this.informationControlCreator;
+		return getRHelpInfoHoverCreator(getInvocationContext());
 	}
 	
 	@Override
-	public Object getAdditionalProposalInfo(final IProgressMonitor monitor) {
+	public @Nullable Object getAdditionalProposalInfo(final IProgressMonitor monitor) {
 		final RHelpManager rHelpManager= RCore.getRHelpManager();
 		final int mode= getMode();
 		
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RHelpTopicCompletionProposal.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RHelpTopicCompletionProposal.java
new file mode 100644
index 0000000..103320d
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RHelpTopicCompletionProposal.java
@@ -0,0 +1,72 @@
+/*=============================================================================#
+ # Copyright (c) 2019 Stephan Wahlbrink <sw@wahlbrink.eu> and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.ui.editors;
+
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.r.core.RSymbolComparator;
+import org.eclipse.statet.r.ui.RUI;
+import org.eclipse.statet.r.ui.sourceediting.RAssistInvocationContext;
+
+
+@NonNullByDefault
+public class RHelpTopicCompletionProposal extends RSimpleCompletionProposal
+		implements ICompletionProposalExtension6 {
+	
+	
+	private ImList<String> pkgNames;
+	
+	
+	public RHelpTopicCompletionProposal(final ProposalParameters<RAssistInvocationContext> parameters,
+			final String replacementString, final @Nullable String pkgName) {
+		super(parameters, replacementString);
+		
+		this.pkgNames= (pkgName != null) ? ImCollections.newList(pkgName) : ImCollections.emptyList();
+	}
+	
+	
+	public void addPackage(final String pkgName) {
+		this.pkgNames= ImCollections.addElementIfAbsent(this.pkgNames, pkgName, RSymbolComparator.R_NAMES_COLLATOR);
+	}
+	
+	
+	@Override
+	public StyledString computeStyledText() {
+		final StyledString styledText= new StyledString(getName());
+		final ImList<String> pkgNames= this.pkgNames;
+		if (!pkgNames.isEmpty()) {
+			styledText.append(QUALIFIER_SEPARATOR, StyledString.QUALIFIER_STYLER);
+			styledText.append(pkgNames.get(0), StyledString.QUALIFIER_STYLER);
+			for (int i= 1; i < pkgNames.size(); i++) {
+				styledText.append(", ", StyledString.QUALIFIER_STYLER); //$NON-NLS-1$
+				styledText.append(pkgNames.get(i), StyledString.QUALIFIER_STYLER);
+			}
+		}
+		return styledText;
+	}
+	
+	@Override
+	public Image getImage() {
+		return RUI.getImage(RUI.IMG_OBJ_R_HELP_TOPIC);
+	}
+	
+}
\ No newline at end of file
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RKeywordCompletionProposal.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RKeywordCompletionProposal.java
deleted file mode 100644
index 405791f..0000000
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RKeywordCompletionProposal.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*=============================================================================#
- # Copyright (c) 2008, 2019 Stephan Wahlbrink and others.
- # 
- # This program and the accompanying materials are made available under the
- # terms of the Eclipse Public License 2.0 which is available at
- # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
- # which is available at https://www.apache.org/licenses/LICENSE-2.0.
- # 
- # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
- # 
- # Contributors:
- #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
- #=============================================================================*/
-
-package org.eclipse.statet.internal.r.ui.editors;
-
-import org.eclipse.swt.graphics.Image;
-
-import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.SimpleCompletionProposal;
-import org.eclipse.statet.r.ui.RUI;
-
-
-public class RKeywordCompletionProposal extends SimpleCompletionProposal {
-	
-	
-	public RKeywordCompletionProposal(final AssistInvocationContext context, final String keyword,
-			final int replacementOffset) {
-		super(context, keyword, replacementOffset);
-	}
-	
-	
-	@Override
-	protected String getPluginId() {
-		return RUI.BUNDLE_ID;
-	}
-	
-	@Override
-	public Image getImage() {
-		return null;
-	}
-	
-	@Override
-	public boolean isAutoInsertable() {
-		return true;
-	}
-	
-}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RPkgCompletionProposal.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RPkgCompletionProposal.java
new file mode 100644
index 0000000..8691563
--- /dev/null
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RPkgCompletionProposal.java
@@ -0,0 +1,84 @@
+/*=============================================================================#
+ # Copyright (c) 2019 Stephan Wahlbrink <sw@wahlbrink.eu> and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.r.ui.editors;
+
+import org.eclipse.jface.text.contentassist.BoldStylerProvider;
+import org.eclipse.jface.viewers.StyledString;
+import org.eclipse.swt.graphics.Image;
+
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+import org.eclipse.statet.ecommons.ui.viewers.StyledTextUtils;
+
+import org.eclipse.statet.r.ui.RUI;
+import org.eclipse.statet.rj.renv.core.RPkgBuilt;
+
+
+@NonNullByDefault
+public class RPkgCompletionProposal extends RElementCompletionProposal {
+	
+	
+	private final @Nullable RPkgBuilt pkgInfo;
+	
+	
+	public RPkgCompletionProposal(final RElementProposalParameters parameters,
+			final @Nullable RPkgBuilt pkgInfo) {
+		super(parameters);
+		
+		this.pkgInfo= pkgInfo;
+	}
+	
+	
+	@Override
+	protected int getMode() {
+		return PACKAGE_NAME;
+	}
+	
+	
+	@Override
+	protected String getName() {
+		return getReplacementName().getSegmentName();
+	}
+	
+	
+	@Override
+	public String getDisplayString() {
+		return getReplacementName().getSegmentName();
+	}
+	
+	@Override
+	public StyledString computeStyledText() {
+		final StyledString styledText= new StyledString(getDisplayString());
+		if (this.pkgInfo != null) {
+			styledText.append(QUALIFIER_SEPARATOR, StyledString.QUALIFIER_STYLER);
+			styledText.append(this.pkgInfo.getTitle(), StyledString.QUALIFIER_STYLER);
+		}
+		return styledText;
+	}
+	
+	@Override
+	protected void styleMatchingRegions(final StyledString styledText,
+			final int matchRule, final int[] matchingRegions,
+			final BoldStylerProvider boldStylerProvider) {
+		StyledTextUtils.setStyle(styledText, matchingRegions, boldStylerProvider.getBoldStyler());
+	}
+	
+	@Override
+	public Image getImage() {
+		return RUI.getImage(RUI.IMG_OBJ_R_PACKAGE);
+	}
+	
+}
\ No newline at end of file
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RQuickRefactoringComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RQuickRefactoringComputer.java
index 2292ae3..b808938 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RQuickRefactoringComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RQuickRefactoringComputer.java
@@ -17,11 +17,10 @@
 import java.util.List;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
 
 import org.eclipse.statet.internal.r.ui.correction.RLinkedNamesAssistProposal;
@@ -31,7 +30,7 @@
 import org.eclipse.statet.ltk.model.core.elements.ISourceStructElement;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IQuickAssistComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.QuickAssistComputer;
 import org.eclipse.statet.r.core.model.IRCompositeSourceElement;
 import org.eclipse.statet.r.core.model.IRLangSourceElement;
 import org.eclipse.statet.r.core.model.IRSourceUnit;
@@ -41,7 +40,8 @@
 import org.eclipse.statet.r.core.rsource.ast.RAstNode;
 
 
-public class RQuickRefactoringComputer implements IQuickAssistComputer {
+@NonNullByDefault
+public class RQuickRefactoringComputer implements QuickAssistComputer {
 	
 	
 	public RQuickRefactoringComputer() {
@@ -49,10 +49,10 @@
 	
 	
 	@Override
-	public IStatus computeAssistProposals(final AssistInvocationContext context,
+	public void computeAssistProposals(final AssistInvocationContext context,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
 		if (!(context.getAstSelection().getCovering() instanceof RAstNode)) {
-			return Status.OK_STATUS;
+			return;
 		}
 		final RAstNode node = (RAstNode) context.getAstSelection().getCovering();
 		
@@ -81,7 +81,6 @@
 		else if (context.getLength() > 0 && context.getSourceUnit() instanceof IRSourceUnit) {
 			proposals.add(new RenameInRegionAssistProposal(context));
 		}
-		return Status.OK_STATUS;
 	}
 	
 	protected void addAccessAssistProposals(final AssistInvocationContext context,
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RSimpleCompletionProposal.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RSimpleCompletionProposal.java
index 0c65eab..d9e83a8 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RSimpleCompletionProposal.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RSimpleCompletionProposal.java
@@ -15,90 +15,37 @@
 package org.eclipse.statet.internal.r.ui.editors;
 
 import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.DocumentEvent;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.IRegion;
-import org.eclipse.jface.text.contentassist.ICompletionProposalExtension6;
-import org.eclipse.jface.viewers.StyledString;
-import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ltk.ui.sourceediting.assist.SimpleCompletionProposal;
-import org.eclipse.statet.r.core.RSymbolComparator;
 import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
-import org.eclipse.statet.r.ui.RUI;
 import org.eclipse.statet.r.ui.sourceediting.RAssistInvocationContext;
 
 
 @NonNullByDefault
-public class RSimpleCompletionProposal extends SimpleCompletionProposal {
+public class RSimpleCompletionProposal extends SimpleCompletionProposal<RAssistInvocationContext> {
 	
 	
-	public static class RHelpTopicCompletionProposal extends RSimpleCompletionProposal
-			implements ICompletionProposalExtension6 {
-		
-		
-		private final int relevance;
-		
-		private final String packages;
-		
-		
-		public RHelpTopicCompletionProposal(final RAssistInvocationContext context,
-				final String replacementString, final String packages, final int replacementOffset,
-				final int relevance) {
-			super(context, replacementString, replacementOffset);
-			
-			this.relevance= relevance;
-			this.packages= packages;
-		}
-		
-		
-		@Override
-		public int getRelevance() {
-			return this.relevance;
-		}
-		
-		@Override
-		public Image getImage() {
-			return RUI.getImage(RUI.IMG_OBJ_R_HELP_TOPIC);
-		}
-		
-		@Override
-		public StyledString getStyledDisplayString() {
-			final StyledString s= new StyledString(getReplacementString());
-			if (this.packages != null) {
-				s.append(QUALIFIER_SEPARATOR, StyledString.QUALIFIER_STYLER);
-				s.append(this.packages, StyledString.QUALIFIER_STYLER);
-			}
-			return s;
-		}
-		
-	}
-	
-	
-	public RSimpleCompletionProposal(final RAssistInvocationContext context,
-			final String replacementString, final int replacementOffset) {
-		super(context, replacementString, replacementOffset);
+	public RSimpleCompletionProposal(final ProposalParameters<RAssistInvocationContext> parameters,
+			final String replacementString) {
+		super(parameters, replacementString);
 	}
 	
 	
 	@Override
-	protected String getPluginId() {
-		return RUI.BUNDLE_ID;
-	}
-	
-	
-	@Override
-	protected int computeReplacementLength(final int replacementOffset, final Point selection, final int caretOffset, final boolean overwrite) {
+	protected int computeReplacementLength(final int replacementOffset, final Point selection,
+			final int caretOffset, final boolean overwrite) {
 		// keep in synch with RElementCompletionProposal
 		final int end= Math.max(caretOffset, selection.x + selection.y);
 		if (overwrite) {
-			final RElementCompletionProposal.ApplyData data= new RElementCompletionProposal.ApplyData(
-					(RAssistInvocationContext) getInvocationContext() );
-			final RHeuristicTokenScanner scanner= data.getScanner();
-			scanner.configure(data.getDocument());
+			final RAssistInvocationContext context= getInvocationContext();
+			final RHeuristicTokenScanner scanner= context.getRHeuristicTokenScanner();
+			scanner.configure(context.getDocument());
 			final IRegion word= scanner.findRWord(end, false, true);
 			if (word != null) {
 				return (word.getOffset() + word.getLength() - replacementOffset);
@@ -107,34 +54,28 @@
 		return (end - replacementOffset);
 	}
 	
-	@Override
-	public boolean isAutoInsertable() {
-		return false;
-	}
 	
 	@Override
-	public boolean validate(final IDocument document, final int offset, final DocumentEvent event) {
+	protected @Nullable String getValidationPrefix(final int offset) throws BadLocationException {
 		// keep in synch with RElementCompletionProposal
-		try {
-			int start= getReplacementOffset();
-			int length= offset - getReplacementOffset();
-			if (length > 0 && document.getChar(start) == '`') {
-				start++;
-				length--;
+		final int startOffset= getReplacementOffset();
+		if (offset >= startOffset) {
+			final RAssistInvocationContext context= getInvocationContext();
+			final IDocument document= context.getDocument();
+			int nameStartOffset= startOffset;
+			int nameEndOffset= offset;
+			if (nameEndOffset > nameStartOffset && document.getChar(nameStartOffset) == '`') {
+				nameStartOffset++;
 			}
-			if (length > 0 && document.getChar(start+length-1) == '`') {
-				length--;
+			if (nameEndOffset > nameStartOffset && document.getChar(nameEndOffset - 1) == '`') {
+				nameEndOffset--;
 			}
-			final String prefix= document.get(start, length);
-			final String replacement= getReplacementString();
-			if (new RSymbolComparator.PrefixPattern(prefix).matches(replacement)) {
-				return true;
+			if (nameEndOffset >= nameStartOffset) {
+				return context.getIdentifierSegmentName(
+						document.get(startOffset, offset - startOffset) );
 			}
 		}
-		catch (final BadLocationException e) {
-			// ignore concurrently modified document
-		}
-		return false;
+		return null;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RoxygenCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RoxygenCompletionComputer.java
index cc0976a..4ad1966 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RoxygenCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/editors/RoxygenCompletionComputer.java
@@ -17,25 +17,29 @@
 import java.util.List;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.source.SourceViewer;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 
 import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.lang.NonNull;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.text.core.SearchPattern;
 
 import org.eclipse.statet.ltk.ui.LTKUI;
 import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IContentAssistComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.SimpleCompletionProposal;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.SourceProposal.ProposalParameters;
 
 
-public class RoxygenCompletionComputer implements IContentAssistComputer {
+@NonNullByDefault
+public class RoxygenCompletionComputer implements ContentAssistComputer {
 	
 	
 	private static final List<String> TAG_COMMANDS;
@@ -92,27 +96,22 @@
 			"field", // since 4.0 (for fields of reference classes)
 		};
 		
-		final String [] commands= new String[tags.length];
+		final String[] commands= new @NonNull String[tags.length];
 		for (int i= 0; i < commands.length; i++) {
 			commands[i]= '@' + tags[i];
 		}
 		TAG_COMMANDS= ImCollections.newList(commands);
 	}
 	
-	private static class TagProposal extends RKeywordCompletionProposal {
+	private static class TagProposal extends SimpleCompletionProposal {
 		
 		
-		public TagProposal(final AssistInvocationContext context, final String keyword, final int replacementOffset) {
-			super(context, keyword, replacementOffset);
+		public TagProposal(final ProposalParameters<?> parameters, final String keyword) {
+			super(parameters, keyword);
 		}
 		
 		
 		@Override
-		public Image getImage() {
-			return LTKUI.getImages().get(LTKUI.OBJ_TEXT_AT_TAG_IMAGE_ID);
-		}
-		
-		@Override
 		protected int computeReplacementLength(final int replacementOffset, final Point selection, final int caretOffset, final boolean overwrite) throws BadLocationException {
 			int end= Math.max(caretOffset, selection.x + selection.y);
 			if (overwrite) {
@@ -128,59 +127,81 @@
 			return (end - replacementOffset);
 		}
 		
+		
 		@Override
-		protected void doApply(final char trigger, final int stateMask,
-				final int caretOffset, final int replacementOffset, final int replacementLength) throws BadLocationException {
+		public Image getImage() {
+			return LTKUI.getImages().get(LTKUI.OBJ_TEXT_AT_TAG_IMAGE_ID);
+		}
+		
+		
+		@Override
+		public boolean isAutoInsertable() {
+			return true;
+		}
+		
+		@Override
+		protected void doApply(final char trigger, final int stateMask, final int caretOffset,
+				final int replacementOffset, final int replacementLength) throws BadLocationException {
 			final AssistInvocationContext context= getInvocationContext();
-			final SourceViewer viewer= context.getSourceViewer();
-			final IDocument document= viewer.getDocument();
-			try {
-				String replacementString= getReplacementString();
-				final int newCaretOffset= replacementOffset+replacementString.length()+1;
-				if (replacementOffset+replacementLength == document.getLength() || document.getChar(replacementOffset+replacementLength) != ' ') {
-					replacementString= replacementString + ' ';
-				}
-				document.replace(replacementOffset, replacementLength, replacementString);
-				setCursorPosition(newCaretOffset);
+			final IDocument document= context.getDocument();
+			final ApplyData applyData= getApplyData();
+			
+			String replacement= getName();
+			final int cursor= replacement.length() + 1;
+			if (replacementOffset + replacementLength == document.getLength()
+					|| document.getChar(replacementOffset + replacementLength) != ' ') {
+				replacement= replacement + ' ';
 			}
-			catch (final BadLocationException e) {
-			}
+			
+			document.replace(replacementOffset, replacementLength, replacement);
+			
+			applyData.setSelection(replacementOffset + cursor);
 		}
 		
 	}
 	
 	
+	private int searchMatchRules;
+	
+	
 	public RoxygenCompletionComputer() {
 	}
 	
 	
 	@Override
-	public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) {
+	public void onSessionStarted(final ISourceEditor editor, final ContentAssist assist) {
+		int matchRules= SearchPattern.PREFIX_MATCH;
+		if (assist.getShowSubstringMatches()) {
+			matchRules |= SearchPattern.SUBSTRING_MATCH;
+		}
+		this.searchMatchRules= matchRules;
 	}
 	
 	@Override
-	public void sessionEnded() {
+	public void onSessionEnded() {
+	}
+	
+	
+	protected int getSearchMatchRules() {
+		return this.searchMatchRules;
 	}
 	
 	@Override
-	public IStatus computeCompletionProposals(final AssistInvocationContext context, final int mode,
+	public void computeCompletionProposals(final AssistInvocationContext context, final int mode,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
 		final String tagPrefix= getTagPrefix(context);
 		if (tagPrefix != null) {
 			doComputeTagProposals(context, tagPrefix, proposals, monitor);
 		}
-		
-		return Status.OK_STATUS;
 	}
 	
 	@Override
-	public IStatus computeInformationProposals(final AssistInvocationContext context,
+	public void computeInformationProposals(final AssistInvocationContext context,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
-		return null;
 	}
 	
 	
-	private String getTagPrefix(final AssistInvocationContext context) {
+	private @Nullable String getTagPrefix(final AssistInvocationContext context) {
 		try {
 			final IDocument document= context.getSourceViewer().getDocument();
 			final int start= Math.max(context.getInvocationOffset() - 20, 0); // max keyword length incl 
@@ -213,12 +234,15 @@
 	}
 	
 	private void doComputeTagProposals(final AssistInvocationContext context, final String prefix,
-		final AssistProposalCollector proposals, final IProgressMonitor monitor) {
-		final int offset= context.getInvocationOffset() - prefix.length();
+			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
+		final ProposalParameters<?> parameters= new ProposalParameters<>(
+				context, context.getInvocationOffset() - prefix.length(),
+				new SearchPattern(getSearchMatchRules(), prefix) );
+		
 		final List<String> keywords= TAG_COMMANDS;
 		for (final String keyword : keywords) {
-			if (keyword.regionMatches(true, 0, prefix, 0, prefix.length())) {
-				proposals.add(new TagProposal(context, keyword, offset));
+			if (parameters.matchesNamePattern(keyword)) {
+				proposals.add(new TagProposal(parameters, keyword));
 			}
 		}
 	}
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpHover.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpHover.java
index 9339c8e..e0e8e02 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpHover.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpHover.java
@@ -50,6 +50,8 @@
 	
 	private final int mode;
 	
+	private @Nullable IInformationControlCreator controlCreator;
+	
 	
 	public RHelpHover() {
 		this(MODE_TOOLTIP);
@@ -190,7 +192,12 @@
 	
 	@Override
 	public IInformationControlCreator getHoverControlCreator() {
-		return new RHelpInfoHoverCreator(this.mode);
+		IInformationControlCreator controlCreator= this.controlCreator;
+		if (controlCreator == null) {
+			controlCreator= new RHelpInfoHoverCreator(this.mode);
+			this.controlCreator= controlCreator;
+		}
+		return controlCreator;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpLabelProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpLabelProvider.java
index 4dcaad5..5ff7a80 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpLabelProvider.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/internal/r/ui/rhelp/RHelpLabelProvider.java
@@ -30,7 +30,7 @@
 import org.eclipse.statet.ecommons.ui.SharedUIResources;
 import org.eclipse.statet.ecommons.workbench.search.ui.TextSearchLabelUtil;
 
-import org.eclipse.statet.ltk.ui.IElementLabelProvider;
+import org.eclipse.statet.ltk.ui.ElementLabelProvider;
 import org.eclipse.statet.r.ui.RUI;
 import org.eclipse.statet.rhelp.core.RHelpKeyword;
 import org.eclipse.statet.rhelp.core.RHelpKeywordGroup;
@@ -100,7 +100,7 @@
 		this.withQualifier= ((style & WITH_QUALIFIER) != 0);
 		this.withTitle= ((style & (WITH_TITLE | TOOLTIP)) != 0);
 		if ((style & HEADER) != 0) {
-			this.defaultStyler= IElementLabelProvider.TITLE_STYLER;
+			this.defaultStyler= ElementLabelProvider.TITLE_STYLER;
 		}
 		else {
 			this.defaultStyler= null;
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
index 4a48ffc..e76fb21 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/RLabelProvider.java
@@ -28,7 +28,9 @@
 import org.eclipse.jface.viewers.ViewerCell;
 import org.eclipse.swt.graphics.Image;
 
+import org.eclipse.statet.jcommons.collections.IntIntervalArrays;
 import org.eclipse.statet.jcommons.collections.IntList;
+import org.eclipse.statet.jcommons.lang.Nullable;
 
 import org.eclipse.statet.ecommons.debug.ui.ECommonsDebugUIResources;
 import org.eclipse.statet.ecommons.models.core.util.ElementPartition;
@@ -37,7 +39,7 @@
 import org.eclipse.statet.ltk.core.ElementName;
 import org.eclipse.statet.ltk.core.LTKUtils;
 import org.eclipse.statet.ltk.model.core.elements.IModelElement;
-import org.eclipse.statet.ltk.ui.IElementLabelProvider;
+import org.eclipse.statet.ltk.ui.ElementLabelProvider;
 import org.eclipse.statet.r.core.data.CombinedRElement;
 import org.eclipse.statet.r.core.data.CombinedRList;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
@@ -66,7 +68,7 @@
  * Label Provider for R elements
  */
 public class RLabelProvider extends StyledCellLabelProvider
-		implements IElementLabelProvider, ILabelProvider {
+		implements ElementLabelProvider, ILabelProvider {
 	
 	
 	public static final int NAMESPACE=                     0x001;
@@ -94,7 +96,7 @@
 		}
 		this.style= style;
 		if ((this.style & HEADER) != 0) {
-			this.defaultStyler= IElementLabelProvider.TITLE_STYLER;
+			this.defaultStyler= ElementLabelProvider.TITLE_STYLER;
 		}
 		else {
 			this.defaultStyler= null;
@@ -284,14 +286,32 @@
 			return getStyledText((IRSourceUnit) element);
 		}
 		else {
-			final String name= element.getElementName().getDisplayName();
-			final StyledString text= (name != null) ?
-					new StyledString(name, this.defaultStyler) :
-					new StyledString();
-			return text;
+			final ElementName elementName= element.getElementName();
+			return new StyledString(elementName.getDisplayName(), this.defaultStyler);
 		}
 	}
 	
+	@Override
+	public int @Nullable [] getStyledTextRegions(final IModelElement element,
+			final int flags, final int[] regions) {
+		if (element instanceof IRElement) {
+			final IRElement rElement= (IRElement) element;
+			return getStyledTextRegions(rElement, rElement.getElementName(),
+					flags, regions );
+		}
+		else if (element instanceof IRSourceUnit) {
+			return getStyledTextRegions((IRSourceUnit) element,
+					flags, regions );
+		}
+		else if (flags == 0) {
+			final ElementName elementName= element.getElementName();
+			if (elementName.getDisplayName().startsWith(elementName.getSegmentName())) {
+				return regions;
+			}
+		}
+		return null;
+	}
+	
 	public StyledString getStyledText(final IRElement element, final RElementName elementName, final RList elementAttr) {
 		final RElementName elementName0= (elementName != null) ? elementName : element.getElementName();
 		final StyledString text= new StyledString();
@@ -304,7 +324,8 @@
 				final String segmentName= elementName0.getSegmentName();
 				int length;
 				if (segmentName != null && ((length= segmentName.length()) > 2)
-						&& (segmentName.charAt(length-2) == '<') && (segmentName.charAt(length-1) == '-')) {
+						&& segmentName.charAt(length - 2) == '<'
+						&& segmentName.charAt(length - 1) == '-' ) {
 					text.append(RElementName.create(RElementName.MAIN_DEFAULT, segmentName.substring(0, length-2)).getDisplayName(), this.defaultStyler);
 					appendMethodDetailAssignmentSpecial(text,  (IRMethod) element);
 					text.append(" <- value"); //$NON-NLS-1$
@@ -384,6 +405,37 @@
 		return text;
 	}
 	
+	public int[] getStyledTextRegions(final IRElement element, final RElementName elementName,
+			final int flags, int[] regions) {
+		final RElementName elementName0= (elementName != null) ? elementName : element.getElementName();
+		
+		switch ((element.getElementType() & MASK_C1) >> SHIFT_C1) {
+		
+		case (IModelElement.C1_METHOD >> SHIFT_C1):
+			if (this.style != 0 && elementName0.getNextSegment() == null) {
+				final String segmentName= elementName0.getSegmentName();
+				int length;
+				if (segmentName != null && ((length= segmentName.length()) > 2)
+						&& segmentName.charAt(length - 2) == '<'
+						&& segmentName.charAt(length - 1) == '-' ) {
+					regions= IntIntervalArrays.removeTail(regions, length - 2);
+					regions= RElementName.create(RElementName.MAIN_DEFAULT, segmentName.substring(0, length-2))
+							.correctDisplayNameRegions(regions, 0);
+					break;
+				}
+			}
+			regions= elementName0.correctDisplayNameRegions(regions, 0);
+			break;
+		
+		default:
+			regions= elementName0.correctDisplayNameRegions(regions, 0);
+			break;
+		
+		}
+		
+		return regions;
+	}
+	
 	private StyledString decorateStyledText(final StyledString text, final CombinedRElement element, final ElementName elementName, final RList elementAttr) {
 		switch (element.getRObjectType()) {
 		case RObject.TYPE_NULL:
@@ -455,10 +507,8 @@
 //	}
 	
 	public StyledString getStyledText(final IRSourceUnit sourceUnit) {
-		final String name= sourceUnit.getElementName().getDisplayName();
-		final StyledString text= (name != null) ?
-				new StyledString(name, this.defaultStyler) :
-				new StyledString();
+		final ElementName elementName= sourceUnit.getElementName();
+		final StyledString text= new StyledString(elementName.getDisplayName(), this.defaultStyler);
 		if ((this.style & RESOURCE_PATH) != 0) {
 			final Object resource= sourceUnit.getResource();
 			if (resource instanceof IResource) {
@@ -468,6 +518,11 @@
 		return text;
 	}
 	
+	public int[] getStyledTextRegions(final IRSourceUnit sourceUnit,
+			final int flags, final int[] regions) {
+		return regions;
+	}
+	
 	public String getText(final ElementPartition partition) {
 		final StringBuilder text= new StringBuilder();
 		text.append("["); //$NON-NLS-1$
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
index 7478bd3..b382f1e 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RAssistInvocationContext.java
@@ -25,7 +25,10 @@
 import org.eclipse.jface.text.ITypedRegion;
 
 import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
+import org.eclipse.statet.jcommons.text.core.input.StringParserInput;
 import org.eclipse.statet.jcommons.ts.core.Tool;
 
 import org.eclipse.statet.ecommons.text.core.IFragmentDocument;
@@ -40,12 +43,14 @@
 import org.eclipse.statet.nico.ui.console.ConsolePageEditor;
 import org.eclipse.statet.r.console.core.RProcess;
 import org.eclipse.statet.r.console.core.util.LoadReferencesUtil;
+import org.eclipse.statet.r.core.IRCoreAccess;
 import org.eclipse.statet.r.core.data.CombinedRElement;
 import org.eclipse.statet.r.core.model.IRSourceUnit;
 import org.eclipse.statet.r.core.model.RElementAccess;
 import org.eclipse.statet.r.core.model.RElementName;
 import org.eclipse.statet.r.core.model.RModel;
 import org.eclipse.statet.r.core.rlang.RTokens;
+import org.eclipse.statet.r.core.rsource.RLexer;
 import org.eclipse.statet.r.core.rsource.ast.FCall;
 import org.eclipse.statet.r.core.rsource.ast.NodeType;
 import org.eclipse.statet.r.core.rsource.ast.RAstNode;
@@ -57,6 +62,7 @@
 /**
  * AssistInvocationContext for R
  */
+@NonNullByDefault
 public class RAssistInvocationContext extends AssistInvocationContext {
 	
 	
@@ -66,7 +72,7 @@
 		
 		private final RElementAccess access;
 		
-		private RFrameSearchPath searchPath;
+		private @Nullable RFrameSearchPath searchPath;
 		
 		
 		public FCallInfo(final FCall node, final RElementAccess access) {
@@ -94,10 +100,12 @@
 		public RFrameSearchPath getSearchPath(final int mode) {
 			final int defaultMode= getDefaultRFrameSearchMode();
 			if (mode == 0 || mode == defaultMode) {
-				if (this.searchPath == null) {
-					this.searchPath= createSearchPath(defaultMode);
+				@Nullable RFrameSearchPath searchPath= this.searchPath;
+				if (searchPath == null) {
+					searchPath= createSearchPath(defaultMode);
+					this.searchPath= searchPath;
 				}
-				return this.searchPath;
+				return searchPath;
 			}
 			else {
 				return createSearchPath(mode);
@@ -133,7 +141,7 @@
 			return sep + 1;
 		}
 		
-		public FCall.Arg getArg(final int argIdx) {
+		public FCall. @Nullable Arg getArg(final int argIdx) {
 			if (argIdx < 0) {
 				return null;
 			}
@@ -150,16 +158,17 @@
 	private static final char[] F_BRACKETS= new char[] { '(', ')' };
 	
 	
-	private RHeuristicTokenScanner scanner;
+	private @Nullable RHeuristicTokenScanner scanner;
+	private @Nullable RLexer lexer;
 	
-	private RElementName prefixName;
+	private @Nullable RElementName prefixName;
 	
 	private int prefixLastSegmentOffset= -1;
 	
 	
-	private final RProcess tool;
+	private final @Nullable RProcess tool;
 	
-	private LoadReferencesUtil toolReferencesUtil;
+	private @Nullable LoadReferencesUtil toolReferencesUtil;
 	
 	
 	public RAssistInvocationContext(final IRSourceEditor editor,
@@ -187,7 +196,7 @@
 	}
 	
 	
-	private RProcess determineRProcess() {
+	private @Nullable RProcess determineRProcess() {
 		final ISourceEditor editor= getEditor();
 		final Tool tool;
 		if (editor instanceof ConsolePageEditor) {
@@ -203,8 +212,9 @@
 	@Override
 	protected boolean reuse(final ISourceEditor editor, final int offset) {
 		if (super.reuse(editor, offset)) {
-			if (this.toolReferencesUtil != null) {
-				this.toolReferencesUtil.setWaitTimeout(getToolReferencesWaitTimeout());
+			LoadReferencesUtil toolReferencesUtil= this.toolReferencesUtil;
+			if (toolReferencesUtil != null) {
+				toolReferencesUtil.setWaitTimeout(getToolReferencesWaitTimeout());
 			}
 			return true;
 		}
@@ -223,10 +233,35 @@
 	}
 	
 	@Override
-	public IRSourceUnit getSourceUnit() {
+	public @Nullable IRSourceUnit getSourceUnit() {
 		return (IRSourceUnit) super.getSourceUnit();
 	}
 	
+	public IRCoreAccess getRCoreAccess() {
+		return getEditor().getRCoreAccess();
+	}
+	
+	
+	public final RHeuristicTokenScanner getRHeuristicTokenScanner() {
+		RHeuristicTokenScanner scanner= this.scanner;
+		if (scanner == null) {
+			scanner= RHeuristicTokenScanner.create(getEditor().getDocumentContentInfo());
+			this.scanner= scanner;
+		}
+		return scanner;
+	}
+	
+	protected RLexer getLexer() {
+		RLexer lexer= this.lexer;
+		if (lexer == null) {
+			lexer= new RLexer((RLexer.DEFAULT |
+					RLexer.SKIP_WHITESPACE | RLexer.SKIP_LINEBREAK | RLexer.SKIP_COMMENT ));
+			lexer.reset(new StringParserInput());
+			this.lexer= lexer;
+		}
+		return lexer;
+	}
+	
 	
 	@Override
 	protected String computeIdentifierPrefix(final int endOffset)
@@ -353,7 +388,7 @@
 		return startOffset;
 	}
 	
-	public RElementName getIdentifierElementName() {
+	public @Nullable RElementName getIdentifierElementName() {
 		if (this.prefixName == null) {
 			this.prefixName= RElementName.parseDefault(getIdentifierPrefix());
 		}
@@ -375,14 +410,20 @@
 	}
 	
 	
-	public final RHeuristicTokenScanner getRHeuristicTokenScanner() {
-		if (this.scanner == null) {
-			this.scanner= RHeuristicTokenScanner.create(getEditor().getDocumentContentInfo());
+	public @Nullable String getIdentifierSegmentName(final String source) {
+		final RLexer lexer= getLexer();
+		((StringParserInput) lexer.getInput()).reset(source).init();
+		lexer.reset();
+		
+		switch (lexer.next()) {
+		case EOF:
+			return "";
+		default:
+			return lexer.getText();
 		}
-		return this.scanner;
 	}
 	
-	private static RElementName getElementAccessOfRegion(final RElementAccess access,
+	private static @Nullable RElementName getElementAccessOfRegion(final RElementAccess access,
 			final TextRegion region) {
 		RElementAccess current= access;
 		while (current != null) {
@@ -417,7 +458,7 @@
 		return null;
 	}
 	
-	public RElementName getNameSelection() {
+	public @Nullable RElementName getNameSelection() {
 		final AstNode selectedNode= getAstSelection().getCovering();
 		if (selectedNode instanceof RAstNode) {
 			RAstNode node= (RAstNode) selectedNode;
@@ -449,7 +490,7 @@
 	}
 	
 	
-	public FCallInfo getFCallInfo() {
+	public @Nullable FCallInfo getFCallInfo() {
 		final RHeuristicTokenScanner scanner= getRHeuristicTokenScanner();
 		
 		int offset= getIdentifierOffset();
@@ -476,7 +517,7 @@
 		return null;
 	}
 	
-	private FCallInfo searchFCallInfo(final int openOffset) {
+	private @Nullable FCallInfo searchFCallInfo(final int openOffset) {
 		final AstInfo astInfo= getAstInfo();
 		if (astInfo == null || astInfo.getRoot() == null) {
 			return null;
@@ -506,7 +547,7 @@
 	}
 	
 	
-	public RProcess getTool() {
+	public @Nullable RProcess getTool() {
 		return this.tool;
 	}
 	
@@ -515,8 +556,11 @@
 	}
 	
 	public LoadReferencesUtil getToolReferencesUtil() {
-		if (this.toolReferencesUtil == null && this.tool != null) {
-			this.toolReferencesUtil= new LoadReferencesUtil(this.tool, getToolReferencesWaitTimeout()) {
+		assert (this.tool != null);
+		
+		LoadReferencesUtil util= this.toolReferencesUtil;
+		if (util == null) {
+			util= new LoadReferencesUtil(this.tool, getToolReferencesWaitTimeout()) {
 				@Override
 				protected void allFinished(final ImList<CombinedRElement> resolvedElements) {
 					if (!resolvedElements.isEmpty()) {
@@ -524,8 +568,9 @@
 					}
 				}
 			};
+			this.toolReferencesUtil= util;
 		}
-		return this.toolReferencesUtil;
+		return util;
 	}
 	
 	protected int getToolReferencesWaitTimeout() {
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
index 0c749e0..467a84f 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RElementCompletionComputer.java
@@ -24,38 +24,40 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.function.BiFunction;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.BadPartitioningException;
 import org.eclipse.jface.text.IDocument;
 
-import org.eclipse.statet.jcommons.collections.CollectionUtils;
 import org.eclipse.statet.jcommons.collections.ImCollections;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
 import org.eclipse.statet.jcommons.lang.Nullable;
+import org.eclipse.statet.jcommons.text.core.SearchPattern;
 
 import org.eclipse.statet.ecommons.text.core.IFragmentDocument;
 import org.eclipse.statet.ecommons.text.core.IPartitionConstraint;
 
 import org.eclipse.statet.internal.r.ui.FCallNamePattern;
 import org.eclipse.statet.internal.r.ui.editors.RElementCompletionProposal;
-import org.eclipse.statet.internal.r.ui.editors.RKeywordCompletionProposal;
+import org.eclipse.statet.internal.r.ui.editors.RElementCompletionProposal.RElementProposalParameters;
+import org.eclipse.statet.internal.r.ui.editors.RHelpTopicCompletionProposal;
+import org.eclipse.statet.internal.r.ui.editors.RPkgCompletionProposal;
 import org.eclipse.statet.internal.r.ui.editors.RSimpleCompletionProposal;
 import org.eclipse.statet.ltk.ast.core.AstNode;
 import org.eclipse.statet.ltk.model.core.elements.IModelElement;
-import org.eclipse.statet.ltk.ui.IElementLabelProvider;
+import org.eclipse.statet.ltk.ui.ElementLabelProvider;
 import org.eclipse.statet.ltk.ui.sourceediting.ISourceEditor;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposal;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IAssistCompletionProposal;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IContentAssistComputer;
-import org.eclipse.statet.r.core.IRCoreAccess;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.SourceProposal.ProposalParameters;
 import org.eclipse.statet.r.core.RCore;
+import org.eclipse.statet.r.core.RSearchPattern;
 import org.eclipse.statet.r.core.RSymbolComparator;
-import org.eclipse.statet.r.core.RSymbolComparator.PrefixPattern;
 import org.eclipse.statet.r.core.data.CombinedRElement;
 import org.eclipse.statet.r.core.model.ArgsDefinition;
 import org.eclipse.statet.r.core.model.IRElement;
@@ -76,7 +78,6 @@
 import org.eclipse.statet.r.core.source.IRDocumentConstants;
 import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
 import org.eclipse.statet.r.ui.RLabelProvider;
-import org.eclipse.statet.r.ui.editors.IRSourceEditor;
 import org.eclipse.statet.r.ui.sourceediting.RAssistInvocationContext.FCallInfo;
 import org.eclipse.statet.r.ui.sourceediting.RFrameSearchPath.RFrameIterator;
 import org.eclipse.statet.rhelp.core.REnvHelp;
@@ -88,11 +89,11 @@
 import org.eclipse.statet.rj.renv.core.REnv;
 import org.eclipse.statet.rj.renv.core.RPkgBuilt;
 import org.eclipse.statet.rj.renv.core.RPkgCompilation;
-import org.eclipse.statet.rj.renv.core.RPkgUtils;
 import org.eclipse.statet.rj.renv.runtime.RPkgManagerDataset;
 
 
-public class RElementCompletionComputer implements IContentAssistComputer {
+@NonNullByDefault
+public class RElementCompletionComputer implements ContentAssistComputer {
 	
 	
 	private static final IPartitionConstraint NO_R_COMMENT_CONSTRAINT= new IPartitionConstraint() {
@@ -103,26 +104,14 @@
 	};
 	
 	
-	private static final List<String> fgKeywords;
+	private static final ImList<String> fgKeywords;
 	static {
 		final ArrayList<String> list= new ArrayList<>();
 		Collections.addAll(list, RTokens.CONSTANT_WORDS);
 		Collections.addAll(list, RTokens.FLOWCONTROL_WORDS);
-		Collections.sort(list, RSymbolComparator.R_NAMES_COLLATOR);
-		list.trimToSize();
-		fgKeywords= Collections.unmodifiableList(list);
+		fgKeywords= ImCollections.toList(list, RSymbolComparator.R_NAMES_COLLATOR);
 	}
 	
-	private static String toString(final Object value) {
-		if (value instanceof List) {
-			final List<String> list= (List<String>) value;
-			Collections.sort(list, RPkgUtils.NAMES_COLLATOR);
-			return CollectionUtils.toString(list, ", "); //$NON-NLS-1$
-		}
-		return value.toString();
-	}
-	
-	
 	public static class CompleteRuntime extends RElementCompletionComputer {
 		
 		public CompleteRuntime() {
@@ -140,12 +129,11 @@
 	protected static final int ARG_TYPE_NO_PRIO= -40;
 	
 	
-	private final IElementLabelProvider labelProvider= new RLabelProvider(RLabelProvider.NAMESPACE);
+	private final ElementLabelProvider labelProvider= new RLabelProvider(RLabelProvider.NAMESPACE);
 	
 	private final int mode;
 	
-	private IRSourceEditor editor;
-	private ContentAssist assist;
+	private int searchMatchRules;
 	
 	private final RFrameSearchPath searchPath= new RFrameSearchPath();
 	
@@ -155,41 +143,28 @@
 	private int pkgNamePrio;
 	private int helpTopicPrio;
 	
-	private IStatus resultStatus;
-	
-	
-	public RElementCompletionComputer() {
-		this(0);
-	}
 	
 	protected RElementCompletionComputer(final int mode) {
 		this.mode= mode;
 	}
 	
-	
-	/**
-	 * {@inheritDoc}
-	 */
-	@Override
-	public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) {
-		this.editor= (editor instanceof IRSourceEditor) ? (IRSourceEditor) editor : null;
-		
-		this.assist= assist;
+	public RElementCompletionComputer() {
+		this(0);
 	}
 	
-	/**
-	 * {@inheritDoc}
-	 */
+	
 	@Override
-	public void sessionEnded() {
+	public void onSessionStarted(final ISourceEditor editor, final ContentAssist assist) {
+		int matchRules= SearchPattern.PREFIX_MATCH;
+		if (assist.getShowSubstringMatches()) {
+			matchRules |= SearchPattern.SUBSTRING_MATCH;
+		}
+		this.searchMatchRules= matchRules;
+	}
+	
+	@Override
+	public void onSessionEnded() {
 		this.searchPath.clear();
-		
-		this.assist= null;
-		this.resultStatus= null;
-	}
-	
-	protected final void setStatus(final IStatus status) {
-		this.resultStatus= status;
 	}
 	
 	
@@ -200,6 +175,10 @@
 		return context.getDefaultRFrameSearchMode();
 	}
 	
+	protected int getSearchMatchRules() {
+		return this.searchMatchRules;
+	}
+	
 	
 	protected final boolean isSymbolCandidate(final String name) {
 		for (int i= 0; i < name.length(); i++) {
@@ -210,7 +189,7 @@
 		return true;
 	}
 	
-	private boolean isCompletable(RElementName elementName) {
+	private boolean isCompletable(@Nullable RElementName elementName) {
 		if (elementName == null) {
 			return false;
 		}
@@ -229,7 +208,7 @@
 		return true;
 	}
 	
-	private RAstNode getRAstNode(final RAssistInvocationContext context) {
+	private @Nullable RAstNode getRAstNode(final RAssistInvocationContext context) {
 		AstNode node= context.getInvocationAstSelection().getCovering();
 		if (node == null) {
 			node= context.getAstInfo().getRoot();
@@ -239,29 +218,21 @@
 	
 	
 	@Override
-	public IStatus computeCompletionProposals(final AssistInvocationContext context,
+	public void computeCompletionProposals(final AssistInvocationContext context,
 			final int mode, final AssistProposalCollector proposals,
 			final IProgressMonitor monitor) {
-		this.resultStatus= null;
-		
 		if (context instanceof RAssistInvocationContext) {
 			computeCompletionProposals((RAssistInvocationContext) context, mode, proposals,
 					monitor );
 		}
-		
-		return this.resultStatus;
 	}
 	
 	@Override
-	public IStatus computeInformationProposals(final AssistInvocationContext context,
+	public void computeInformationProposals(final AssistInvocationContext context,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
-		this.resultStatus= null;
-		
 		if (context instanceof RAssistInvocationContext) {
 			doComputeContextProposals((RAssistInvocationContext) context, proposals, monitor);
 		}
-		
-		return this.resultStatus;
 	}
 	
 	protected void computeCompletionProposals(final RAssistInvocationContext context, final int mode,
@@ -326,7 +297,7 @@
 	}
 	
 	
-	private String checkHelp(final RAssistInvocationContext context) {
+	private @Nullable String checkHelp(final RAssistInvocationContext context) {
 		try {
 			if (context.getIdentifierOffset() > 0
 					&& context.getDocument().getChar(context.getIdentifierOffset() - 1) == '?') {
@@ -423,13 +394,17 @@
 		
 		this.searchPath.init(context, node, getSearchMode(context), prefixName.getScope());
 		
-		final RSymbolComparator.PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName); 
-		final int offset= context.getIdentifierLastSegmentOffset();
+		final RElementProposalParameters parameters= new RElementProposalParameters(
+				context, context.getIdentifierLastSegmentOffset(),
+				new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
+				this.labelProvider );
+		
 		final Set<String> mainNames= new HashSet<>();
 		final List<String> methodNames= new ArrayList<>();
 		
 		for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
 			final IRFrame envir= iter.next();
+			parameters.baseRelevance= iter.getRelevance();
 			
 			final List<? extends IRElement> elements= envir.getModelChildren(null);
 			for (final IRElement element : elements) {
@@ -438,25 +413,24 @@
 				final boolean isRich= (c1type == IModelElement.C1_METHOD);
 				if ((isRich || c1type == IModelElement.C1_VARIABLE)
 						&& isCompletable(elementName)
-						&& pattern.matches(elementName.getSegmentName())) {
-					final int relevance= iter.getRelevance();
-					if ((relevance < 0) && !isRich
+						&& parameters.matchesNamePattern(elementName.getSegmentName()) ) {
+					if ((parameters.baseRelevance < 0) && !isRich
 							&& mainNames.contains(elementName.getSegmentName()) ) {
 						continue;
 					}
-					final IAssistCompletionProposal proposal= createProposal(context,
-							offset, elementName, element, relevance );
-					if (proposal != null) {
-						if (elementName.getNextSegment() == null) {
-							if (isRich) {
-								methodNames.add(elementName.getSegmentName());
-							}
-							else {
-								mainNames.add(elementName.getSegmentName());
-							}
+					parameters.replacementName= elementName;
+					parameters.element= element;
+					
+					final AssistProposal proposal= new RElementCompletionProposal(parameters);
+					if (elementName.getNextSegment() == null) {
+						if (isRich) {
+							methodNames.add(elementName.getSegmentName());
 						}
-						proposals.add(proposal);
+						else {
+							mainNames.add(elementName.getSegmentName());
+						}
 					}
+					proposals.add(proposal);
 				}
 			}
 		}
@@ -464,21 +438,22 @@
 		mainNames.addAll(methodNames);
 		for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
 			final IRFrame envir= iter.next();
+			parameters.baseRelevance= 0;
+			
 			if (envir instanceof IRFrameInSource) {
 				final IRFrameInSource sframe= (IRFrameInSource) envir;
 				final Set<String> elementNames= sframe.getAllAccessNames();
 				for (final String candidate : elementNames) {
 					if (candidate != null
-							&& pattern.matches(candidate) 
-							&& !mainNames.contains(candidate)
 							&& !(candidate.equals(prefixSegmentName)
-									&& (sframe.getAllAccessOf(candidate, false).size() <= 1) )) {
-						final IAssistCompletionProposal proposal= createProposal(context,
-								offset, candidate );
-						if (proposal != null) {
-							mainNames.add(candidate);
-							proposals.add(proposal);
-						}
+									&& (sframe.getAllAccessOf(candidate, false).size() <= 1) )
+							&& !mainNames.contains(candidate)
+							&& parameters.matchesNamePattern(candidate) ) {
+						
+						final AssistProposal proposal= new RSimpleCompletionProposal(parameters,
+								candidate );
+						mainNames.add(candidate);
+						proposals.add(proposal);
 					}
 				}
 			}
@@ -497,11 +472,14 @@
 		
 		final String prefixSource= context.getIdentifierPrefix();
 		if (!prefixSegmentName.isEmpty() && prefixSource.charAt(0) != '`') {
-			final int offset= context.getIdentifierOffset();
+			final ProposalParameters<RAssistInvocationContext> parameters= new ProposalParameters<>(
+					context, context.getIdentifierOffset(),
+					new RSearchPattern(SearchPattern.PREFIX_MATCH, prefixSegmentName) );
+			
 			final List<String> keywords= fgKeywords;
 			for (final String keyword : keywords) {
-				if (keyword.regionMatches(true, 0, prefixSegmentName, 0, prefixSegmentName.length())) {
-					proposals.add(new RKeywordCompletionProposal(context, keyword, offset));
+				if (parameters.matchesNamePattern(keyword)) {
+					proposals.add(new RSimpleCompletionProposal(parameters, keyword));
 				}
 			}
 		}
@@ -531,14 +509,18 @@
 			namePrefix= (prefixSegment.getSegmentName() != null) ?
 					prefixSegment.getSegmentName() : ""; //$NON-NLS-1$
 		}
-		final RSymbolComparator.PrefixPattern pattern= new RSymbolComparator.PrefixPattern(namePrefix);
-		final int offset= context.getIdentifierLastSegmentOffset();
+		
+		final RElementProposalParameters parameters= new RElementProposalParameters(
+				context, context.getIdentifierLastSegmentOffset(),
+				new RSearchPattern(getSearchMatchRules(), namePrefix),
+				this.labelProvider );
 		
 		final Set<String> mainNames= new HashSet<>();
 		final List<String> methodNames= new ArrayList<>();
 		
 		for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
 			final IRFrame envir= iter.next();
+			parameters.baseRelevance= iter.getRelevance();
 			
 			final List<? extends IRLangElement> elements= envir.getModelChildren(null);
 			ITER_ELEMENTS: for (final IRLangElement rootElement : elements) {
@@ -590,26 +572,25 @@
 							elementSegment= child.getElementName();
 						}
 						final String candidate= elementSegment.getSegmentName();
-						final int relevance= iter.getRelevance();
 						if (isCompletable(elementSegment)
-								&& pattern.matches(candidate) ) {
-							if ((relevance > 0) && !isRich
+								&& parameters.matchesNamePattern(candidate) ) {
+							if ((parameters.baseRelevance > 0) && !isRich
 									&& mainNames.contains(candidate) ) {
 								continue ITER_ELEMENTS;
 							}
-							final IAssistCompletionProposal proposal= createProposal(context,
-									offset, elementSegment, child, relevance );
-							if (proposal != null) {
-								if (elementSegment.getNextSegment() == null) {
-									if (isRich) {
-										methodNames.add(candidate);
-									}
-									else {
-										mainNames.add(candidate);
-									}
+							parameters.replacementName= elementSegment;
+							parameters.element= child;
+							
+							final AssistProposal proposal= new RElementCompletionProposal(parameters);
+							if (elementSegment.getNextSegment() == null) {
+								if (isRich) {
+									methodNames.add(candidate);
 								}
-								proposals.add(proposal);
+								else {
+									mainNames.add(candidate);
+								}
 							}
+							proposals.add(proposal);
 						}
 					}
 				}
@@ -619,6 +600,8 @@
 		mainNames.addAll(methodNames);
 		for (final RFrameIterator iter= this.searchPath.iterator(); iter.hasNext();) {
 			final IRFrame envir= iter.next();
+			parameters.baseRelevance= 0;
+			
 			if (envir instanceof IRFrameInSource) {
 				final IRFrameInSource sframe= (IRFrameInSource) envir;
 				final List<? extends RElementAccess> allAccess= sframe.getAllAccessOf(
@@ -642,15 +625,14 @@
 						}
 						final String candidate= elementSegment.getSegmentName();
 						if (candidate != null && isCompletable(elementSegment)
-								&& pattern.matches(candidate)
+								&& !candidate.equals(namePrefix)
 								&& !mainNames.contains(candidate)
-								&& !candidate.equals(namePrefix) ) {
-							final IAssistCompletionProposal proposal= createProposal(context,
-									offset, candidate );
-							if (proposal != null) {
-								mainNames.add(candidate);
-								proposals.add(proposal);
-							}
+								&& parameters.matchesNamePattern(candidate) ) {
+							
+							final AssistProposal proposal= new RSimpleCompletionProposal(parameters,
+									candidate );
+							mainNames.add(candidate);
+							proposals.add(proposal);
 						}
 					}
 				}
@@ -658,18 +640,6 @@
 		}
 	}
 	
-	protected IAssistCompletionProposal createProposal(final RAssistInvocationContext context,
-			final int offset, final String name) {
-		return new RSimpleCompletionProposal(context, name, offset);
-	}
-	
-	protected IAssistCompletionProposal createProposal(final RAssistInvocationContext context,
-			final int offset, final RElementName elementName, final IRElement element,
-			final int relevance) {
-		return new RElementCompletionProposal(context, elementName, offset, element, relevance,
-				this.labelProvider );
-	}
-	
 	public void doComputeContextProposals(final RAssistInvocationContext context,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
 		if (context.getModelInfo() == null) {
@@ -678,17 +648,20 @@
 		
 		final RAssistInvocationContext.FCallInfo fCallInfo= context.getFCallInfo();
 		if (fCallInfo != null) {
+			final RElementProposalParameters parameters= new RElementProposalParameters(
+					context, Math.max(fCallInfo.getNode().getArgsOpenOffset() + 1, 0),
+					null,
+					this.labelProvider );
+			
 			final FCallNamePattern pattern= new FCallNamePattern(fCallInfo.getAccess()) {
-				
-				final int infoOffset= Math.max(fCallInfo.getNode().getArgsOpenOffset() + 1, 0);
-				
 				@Override
 				protected void handleMatch(final IRMethod element, final IRFrame frame,
 						final RFrameIterator iterator) {
-					proposals.add(new RElementCompletionProposal.ContextInformationProposal(context,
-									element.getElementName(), this.infoOffset, element,
-									iterator.getRelevance(),
-									RElementCompletionComputer.this.labelProvider ));
+					parameters.baseRelevance= iterator.getRelevance();
+					parameters.replacementName= element.getElementName();
+					parameters.element= element;
+					
+					proposals.add(new RElementCompletionProposal.ContextInformationProposal(parameters));
 				}
 			};
 			pattern.searchFDef(fCallInfo.getSearchPath(getSearchMode(context)));
@@ -706,8 +679,6 @@
 		final RElementName prefixName= context.getIdentifierElementName();
 		final String prefixSegmentName= nonNullAssert(prefixName.getSegmentName());
 		
-		final int offset= context.getIdentifierOffset();
-		
 		if (argValue && !argName) {
 			final FCall.Arg arg= fCallInfo.getArg(argIdx);
 			if (arg != null && arg.hasName()) {
@@ -717,9 +688,14 @@
 			}
 		}
 		
+		final RElementProposalParameters parameters= new RElementProposalParameters(
+				context, context.getIdentifierOffset(),
+				new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
+				this.labelProvider );
+		
 		class FCallHandler extends FCallNamePattern {
 			
-			private final RCoreFunctions coreFunction;
+			private final @Nullable RCoreFunctions coreFunction;
 			
 			private final HashSet<String> argNames= new HashSet<>();
 			
@@ -794,40 +770,41 @@
 			
 			@Override
 			protected void handleMatch(final IRMethod element, final IRFrame frame,
-					final RFrameIterator iterator) {
+					final RFrameIterator iter) {
 				final ArgsDefinition argsDef= element.getArgsDefinition();
 				if (argsDef == null) {
 					return;
 				}
 				
+				final int contextRelevance= iter.getRelevance();
 				this.matchCount++;
 				
-				final int relevance= iterator.getRelevance();
 				if (argName) {
 					for (int i= 0; i < argsDef.size(); i++) {
 						final ArgsDefinition.Arg arg= argsDef.get(i);
 						if (arg.name != null && arg.name.length() > 0 && !arg.name.equals("...")) {
-							if ((prefixSegmentName == null || arg.name.startsWith(prefixSegmentName))
+							if (parameters.matchesNamePattern(prefixSegmentName)
 									&& this.argNames.add(arg.name)) {
 								final RElementName name= RElementName.create(RElementName.MAIN_DEFAULT, arg.name);
-								proposals.add(new RElementCompletionProposal.ArgumentProposal(
-										context, name, offset, element,
-										ARG_NAME_PRIO + relevance,
-										RElementCompletionComputer.this.labelProvider ));
+								parameters.baseRelevance= contextRelevance + ARG_NAME_PRIO;
+								parameters.replacementName= name;
+								parameters.element= element;
+								
+								proposals.add(new RElementCompletionProposal.ArgumentProposal(parameters));
 							}
 						}
 					}
 				}
 				if (argValue) {
 					final FCall.Args callArgs= fCallInfo.getNode().getArgsChild();
-					if (!checkArgsDef(RAst.matchArgs(callArgs, argsDef), true, relevance)
+					if (!checkArgsDef(RAst.matchArgs(callArgs, argsDef), true, contextRelevance)
 							&& frame.getFrameType() == IRFrame.PACKAGE
 							&& this.coreFunction != null
 							&& this.coreFunction.getPackageNames().contains(frame.getElementName().getSegmentName()) ) {
 						final ArgsDefinition coreDef= this.coreFunction.getArgs(
 								getElementName().getSegmentName() );
 						if (coreDef != null) {
-							checkArgsDef(RAst.matchArgs(callArgs, coreDef), false, relevance);
+							checkArgsDef(RAst.matchArgs(callArgs, coreDef), false, contextRelevance);
 						}
 					}
 				}
@@ -839,12 +816,8 @@
 	}
 	
 	
-	protected IRCoreAccess getRCoreAccess() {
-		return this.editor.getRCoreAccess();
-	}
-	
-	protected final @Nullable RPkgManagerDataset getRPkgDataset() {
-		final IRPkgManager manager= RCore.getRPkgManager(getRCoreAccess().getREnv());
+	protected final @Nullable RPkgManagerDataset getRPkgDataset(final RAssistInvocationContext context) {
+		final IRPkgManager manager= RCore.getRPkgManager(context.getRCoreAccess().getREnv());
 		if (manager != null) {
 			return manager.getDataset();
 		}
@@ -867,20 +840,24 @@
 			return;
 		}
 		
-		final RPkgManagerDataset rPkgDataset= getRPkgDataset();
+		final RElementProposalParameters parameters= new RElementProposalParameters(
+				context, context.getInvocationOffset() - prefixSegmentName.length(),
+				new RSearchPattern(getSearchMatchRules(), prefixSegmentName), prio,
+				this.labelProvider );
 		
-		final PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName);
-		final int offset= context.getInvocationOffset() - prefixSegmentName.length();
+		final RPkgManagerDataset rPkgDataset= getRPkgDataset(context);
 		
 		final Collection<String> envNames;
 		if (rPkgDataset != null) {
 			final RPkgCompilation<? extends RPkgBuilt> pkgs= rPkgDataset.getInstalled();
 			envNames= pkgs.getNames();
 			for (final String pkgName : envNames) {
-				if (pattern.matches(pkgName)) {
-					proposals.add(new RElementCompletionProposal.RPkgProposal(context, 
-							RElementName.create(RElementName.SCOPE_PACKAGE, pkgName), offset,
-							pkgs.getFirst(pkgName), prio ));
+				if (parameters.matchesNamePattern(pkgName)) {
+					parameters.replacementName= RElementName.create(RElementName.SCOPE_PACKAGE, pkgName);
+					
+					final AssistProposal proposal= new RPkgCompletionProposal(parameters,
+							pkgs.getFirst(pkgName) );
+					proposals.add(proposal);
 				}
 			}
 		}
@@ -890,16 +867,17 @@
 		
 		final Set<String> workspaceNames= RModel.getRModelManager().getPkgNames();
 		for (final String pkgName : workspaceNames) {
-			if (!envNames.contains(pkgName) && pattern.matches(pkgName)) {
-				proposals.add(new RElementCompletionProposal.RPkgProposal(context, 
-						RElementName.create(RElementName.SCOPE_PACKAGE, pkgName), offset,
-						null, prio ));
+			if (!envNames.contains(pkgName)
+					&& parameters.matchesNamePattern(pkgName) ) {
+				parameters.replacementName= RElementName.create(RElementName.SCOPE_PACKAGE, pkgName);
+				
+				proposals.add(new RPkgCompletionProposal(parameters, null));
 			}
 		}
 	}
 	
 	protected final void doComputeHelpTopicProposals(final RAssistInvocationContext context,
-			final String topicType, final RAstNode node,
+			final @Nullable String topicType, final RAstNode node,
 			final int prio, final AssistProposalCollector proposals) {
 		// (topic != null) => ?  /  (topic == null) => help()
 		final RElementName prefixName= context.getIdentifierElementName();
@@ -909,32 +887,23 @@
 			return;
 		}
 		
-		final REnv rEnv= getRCoreAccess().getREnv();
+		final REnv rEnv= context.getRCoreAccess().getREnv();
 		if (rEnv == null) {
 			return;
 		}
 		
-		final Map<String, Object> matches= new HashMap<>();
-		
 		final RHelpManager rHelpManager= RCore.getRHelpManager();
 		
 		final REnvHelp help= rHelpManager.getHelp(rEnv);
 		if (help != null) {
 			try {
-				final PrefixPattern pattern= new RSymbolComparator.PrefixPattern(prefixSegmentName);
-				final BiFunction<Object, Object, Object> mergeFun=
-						(final Object oldValue, final Object newValue) -> {
-							List<Object> list;
-							if (oldValue instanceof List) {
-								list= (List<Object>) oldValue;
-							}
-							else {
-								list= new ArrayList<>(4);
-								list.add(oldValue);
-							}
-							list.add(newValue);
-							return list;
-						};
+				final ProposalParameters<RAssistInvocationContext> parameters= new ProposalParameters<>(
+						context, (context.getInvocationContentType() != IRDocumentConstants.R_DEFAULT_CONTENT_TYPE) ?
+								context.getIdentifierLastSegmentOffset() + 1 :
+								context.getIdentifierLastSegmentOffset(),
+						new RSearchPattern(getSearchMatchRules(), prefixSegmentName),
+						prio );
+				final Map<String, RHelpTopicCompletionProposal> map= new HashMap<>();
 				this.searchPath.init(context, node, getSearchMode(context), prefixName.getScope());
 				final Set<String> pkgNames= new HashSet<>();
 				for (final IRFrame frame : this.searchPath) {
@@ -945,8 +914,18 @@
 							if (pkgHelp != null) {
 								for (final RHelpTopicEntry topicEntry : pkgHelp.getTopics()) {
 									final String topic= topicEntry.getTopic();
-									if (pattern.matches(topic)) {
-										matches.merge(topic, pkgName, mergeFun);
+									if (parameters.matchesNamePattern(topic)) {
+										
+										RHelpTopicCompletionProposal proposal= map.get(topic);
+										if (proposal == null) {
+											proposal= new RHelpTopicCompletionProposal(
+													parameters, topic, pkgName );
+											map.put(topic, proposal);
+											proposals.add(proposal);
+										}
+										else {
+											proposal.addPackage(pkgName);
+										}
 									}
 								}
 							}
@@ -958,15 +937,6 @@
 				help.unlock();
 			}
 		}
-		
-		final int offset= (context.getInvocationContentType() != IRDocumentConstants.R_DEFAULT_CONTENT_TYPE) ?
-				context.getIdentifierLastSegmentOffset() + 1 :
-				context.getIdentifierLastSegmentOffset();
-		for (final Map.Entry<String, Object> match : matches.entrySet()) {
-			proposals.add(new RSimpleCompletionProposal.RHelpTopicCompletionProposal(
-					context, match.getKey(), toString(match.getValue()),
-					offset, prio ));
-		}
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPathCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPathCompletionComputer.java
index a1a2d65..88516de 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPathCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPathCompletionComputer.java
@@ -23,13 +23,13 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.ITypedRegion;
 import org.eclipse.jface.text.TextUtilities;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.text.core.BasicTextRegion;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
 import org.eclipse.statet.jcommons.ts.core.Tool;
@@ -46,10 +46,10 @@
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.PathCompletionComputor;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.SourceProposal.ProposalParameters;
 import org.eclipse.statet.nico.ui.console.ConsolePageEditor;
 import org.eclipse.statet.r.console.core.RProcess;
 import org.eclipse.statet.r.core.RUtil;
-import org.eclipse.statet.r.ui.RUI;
 
 
 /**
@@ -57,13 +57,14 @@
  * 
  * Supports workspace properties of a R tool process
  */
+@NonNullByDefault
 public class RPathCompletionComputer extends PathCompletionComputor {
 	
 	
-	private RProcess associatedTool;
+	private @Nullable RProcess associatedTool;
 	
-	private IContainer baseResource;
-	private IFileStore baseFileStore;
+	private @Nullable IContainer baseResource;
+	private @Nullable IFileStore baseFileStore;
 	
 	
 	public RPathCompletionComputer() {
@@ -71,12 +72,7 @@
 	
 	
 	@Override
-	public String getPluginId() {
-		return RUI.BUNDLE_ID;
-	}
-	
-	@Override
-	public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) {
+	public void onSessionStarted(final ISourceEditor editor, final ContentAssist assist) {
 		this.associatedTool= null;
 		this.baseResource= null;
 		this.baseFileStore= null;
@@ -108,38 +104,40 @@
 			}
 		}
 		
-		super.sessionStarted(editor, assist);
+		super.onSessionStarted(editor, assist);
 	}
 	
 	@Override
-	public void sessionEnded() {
-		super.sessionEnded();
+	public void onSessionEnded() {
+		super.onSessionEnded();
 		this.associatedTool= null;
 	}
 	
 	@Override
 	protected boolean getIsWindows() {
-		if ((this.associatedTool != null)) {
-			return this.associatedTool.getWorkspaceData().isWindows();
+		final RProcess associatedTool= this.associatedTool;
+		if ((associatedTool != null)) {
+			return associatedTool.getWorkspaceData().isWindows();
 		}
 		return super.getIsWindows();
 	}
 	
 	@Override
 	protected char getDefaultFileSeparator() {
-		if (this.associatedTool != null) {
-			return this.associatedTool.getWorkspaceData().getFileSeparator();
+		final RProcess associatedTool= this.associatedTool;
+		if (associatedTool != null) {
+			return associatedTool.getWorkspaceData().getFileSeparator();
 		}
 		return super.getDefaultFileSeparator();
 	}
 	
 	@Override
-	protected TextRegion getContentRange(final AssistInvocationContext context, final int mode)
+	protected @Nullable TextRegion getContentRange(final AssistInvocationContext context, final int mode)
 			throws BadLocationException {
 		final IDocument document= context.getSourceViewer().getDocument();
 		final int offset= context.getInvocationOffset();
 		final ITypedRegion partition= TextUtilities.getPartition(document,
-				getEditor().getDocumentContentInfo().getPartitioning(), offset, true);
+				context.getEditor().getDocumentContentInfo().getPartitioning(), offset, true);
 		int start= partition.getOffset();
 		int end= partition.getOffset() + partition.getLength();
 		if (start == end) {
@@ -167,49 +165,63 @@
 	}
 	
 	@Override
-	protected IPath getRelativeBasePath() {
-		if (this.associatedTool != null) {
-			final IFileStore wd= this.associatedTool.getWorkspaceData().getWorkspaceDir();
-			if (wd != null) {
-				return URIUtil.toPath(wd.toURI());
+	protected @Nullable IPath getRelativeBasePath() {
+		{	final RProcess associatedTool= this.associatedTool;
+			if (associatedTool != null) {
+				final IFileStore wd= associatedTool.getWorkspaceData().getWorkspaceDir();
+				if (wd != null) {
+					return URIUtil.toPath(wd.toURI());
+				}
+				return null;
 			}
-			return null;
 		}
-		if (this.baseResource != null) {
-			return this.baseResource.getLocation();
+		{	final IFileStore baseFileStore= this.baseFileStore;
+			if (baseFileStore != null) {
+				return this.baseResource.getLocation();
+			}
 		}
 		return null;
 	}
 	
 	@Override
-	protected IFileStore getRelativeBaseStore() {
-		if (this.associatedTool != null) {
-			return this.associatedTool.getWorkspaceData().getWorkspaceDir();
+	protected @Nullable IFileStore getRelativeBaseStore() {
+		{	final RProcess associatedTool= this.associatedTool;
+			if (associatedTool != null) {
+				return associatedTool.getWorkspaceData().getWorkspaceDir();
+			}
 		}
-		if (this.baseFileStore != null) {
-			return this.baseFileStore;
+		{	final IFileStore baseFileStore= this.baseFileStore;
+			if (baseFileStore != null) {
+				return baseFileStore;
+			}
 		}
 		return null;
 	}
 	
 	@Override
-	protected IFileStore resolveStore(final IPath path) throws CoreException {
-		if (this.associatedTool != null) {
-			return this.associatedTool.getWorkspaceData().toFileStore(path);
+	protected @Nullable IFileStore resolveStore(final IPath path) throws CoreException {
+		{	final RProcess associatedTool= this.associatedTool;
+			if (associatedTool != null) {
+				return associatedTool.getWorkspaceData().toFileStore(path);
+			}
 		}
 		return super.resolveStore(path);
 	}
 	
 	@Override
-	protected IStatus tryAlternative(final AssistInvocationContext context,
+	protected void tryAlternative(final AssistInvocationContext context,
 			final IPath path,
 			final int startOffset, final String segmentPrefix, final String completionPrefix,
 			final AssistProposalCollector proposals) throws CoreException {
-		if (this.associatedTool != null) {
-			final String address= this.associatedTool.getWorkspaceData().getRemoteAddress();
+		final RProcess associatedTool= this.associatedTool;
+		if (associatedTool != null) {
+			final String address= associatedTool.getWorkspaceData().getRemoteAddress();
 			if (address != null) {
 				final IResourceMappingManager rmManager= ResourceMappingUtils.getManager();
 				if (rmManager != null) {
+					final ProposalParameters<?> parameters= createProposalParameters(
+							context, startOffset, segmentPrefix );
+					
 					final List<IResourceMapping> mappings= rmManager
 							.getResourceMappingsFor(address, ResourceMappingOrder.REMOTE);
 					for (final IResourceMapping mapping : mappings) {
@@ -223,24 +235,24 @@
 						else {
 							continue;
 						}
-						final String name= remotePath.segment(0);
-						if (segmentPrefix.isEmpty()
-								|| (name != null && name.regionMatches(true, 0, segmentPrefix, 0, segmentPrefix.length())) ) {
-							proposals.add(new ResourceProposal(context, startOffset,
+						String name= remotePath.segment(0);
+						if (name == null) {
+							name= ""; //$NON-NLS-1$
+						}
+						if (parameters.matchesNamePattern(name)) {
+							proposals.add(new ResourceCompletionProposal(parameters,
 									mapping.getFileStore(), remotePath.toString(), completionPrefix,
 									null ));
 						}
 					}
-					return Status.OK_STATUS;
+					return;
 				}
 			}
 		}
-		return super.tryAlternative(context, path, startOffset, segmentPrefix,
-				completionPrefix, proposals );
 	}
 	
 	@Override
-	protected String checkPrefix(final String prefix) {
+	protected @Nullable String checkPrefix(final String prefix) {
 		String unescaped= RUtil.unescapeCompletely(prefix);
 		// keep a single (not escaped) backslash
 		if (prefix.length() > 0 && prefix.charAt(prefix.length()-1) == '\\' && 
@@ -251,19 +263,22 @@
 	}
 	
 	@Override
-	protected String checkPathCompletion(final IDocument document, final int completionOffset, String completion)
-			throws BadLocationException {
+	protected String checkPathCompletion(final IDocument document, final int completionOffset,
+			String completion) {
 		completion= RUtil.escapeCompletely(completion);
 		int existingBackslashCount= 0;
 		if (completionOffset >= 1) {
-			if (document.getChar(completionOffset-1) == '\\') {
-				existingBackslashCount++;
-				if (completionOffset >= 2) {
-					if (document.getChar(completionOffset-2) == '\\') {
-						existingBackslashCount++;
+			try {
+				if (document.getChar(completionOffset - 1) == '\\') {
+					existingBackslashCount++;
+					if (completionOffset >= 2) {
+						if (document.getChar(completionOffset - 2) == '\\') {
+							existingBackslashCount++;
+						}
 					}
 				}
 			}
+			catch (final BadLocationException e) {}
 		}
 		final boolean startsWithBackslash= (completion.length() >= 2 && 
 				completion.charAt(0) == '\\' && completion.charAt(1) == '\\');
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPkgCompletionComputer.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPkgCompletionComputer.java
index e051ff4..18231de 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPkgCompletionComputer.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RPkgCompletionComputer.java
@@ -15,7 +15,6 @@
 package org.eclipse.statet.r.ui.sourceediting;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
 
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
@@ -41,9 +40,8 @@
 	}
 	
 	@Override
-	public IStatus computeInformationProposals(final AssistInvocationContext context,
+	public void computeInformationProposals(final AssistInvocationContext context,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
-		return null;
 	}
 	
 }
diff --git a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RQuickAssistProcessor.java b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RQuickAssistProcessor.java
index 935a3f9..3336a4d 100644
--- a/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RQuickAssistProcessor.java
+++ b/r/org.eclipse.statet.r.ui/src/org/eclipse/statet/r/ui/sourceediting/RQuickAssistProcessor.java
@@ -20,7 +20,7 @@
 import org.eclipse.statet.internal.r.ui.editors.RQuickRefactoringComputer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IQuickAssistComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.QuickAssistComputer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.QuickAssistProcessor;
 import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
 import org.eclipse.statet.r.ui.editors.IRSourceEditor;
@@ -29,7 +29,7 @@
 public class RQuickAssistProcessor extends QuickAssistProcessor {
 	
 	
-	private final IQuickAssistComputer computer= new RQuickRefactoringComputer();
+	private final QuickAssistComputer computer= new RQuickRefactoringComputer();
 	
 	private RHeuristicTokenScanner scanner;
 	
diff --git a/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/internal/redocs/tex/r/ui/sourceediting/RChunkTemplateCompletionComputer.java b/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/internal/redocs/tex/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
index 652b381..b37ab5d 100644
--- a/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/internal/redocs/tex/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
+++ b/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/internal/redocs/tex/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
@@ -16,13 +16,14 @@
 
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.jface.text.templates.Template;
 import org.eclipse.jface.text.templates.TemplateContextType;
 import org.eclipse.swt.graphics.Image;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
 
 import org.eclipse.statet.internal.redocs.tex.r.RedocsTexRPlugin;
@@ -30,9 +31,11 @@
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistInvocationContext;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.AssistProposalCollector;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.TemplateCompletionComputer;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.TemplateProposal.TemplateProposalParameters;
 import org.eclipse.statet.redocs.r.ui.RedocsRUIResources;
 
 
+@NonNullByDefault
 public class RChunkTemplateCompletionComputer extends TemplateCompletionComputer {
 	
 	
@@ -43,44 +46,49 @@
 	
 	
 	@Override
-	public IStatus computeCompletionProposals(final AssistInvocationContext context, int mode,
+	public void computeCompletionProposals(final AssistInvocationContext context, int mode,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
 		// Set to specific mode to force to include templates in default mode
 		if (mode == COMBINED_MODE) {
 			mode= SPECIFIC_MODE;
 		}
 		
-		return super.computeCompletionProposals(context, mode, proposals, monitor);
+		super.computeCompletionProposals(context, mode, proposals, monitor);
 	}
 	
 	@Override
-	protected boolean include(final Template template, final String prefix) {
+	protected boolean include(final Template template, final TemplateProposalParameters<?> parameters) {
+		parameters.matchRule= OTHER_MATCH;
 		return true;
 	}
 	
 	@Override
-	protected String extractPrefix(final AssistInvocationContext context) {
+	protected @Nullable String extractPrefix(final AssistInvocationContext context) {
 		final IDocument document= context.getSourceViewer().getDocument();
 		final int offset= context.getOffset();
 		try {
 			final int lineOffset= document.getLineOffset(document.getLineOfOffset(offset));
-			if (offset-lineOffset == 2) {
-				if (document.getChar(lineOffset) == '<' && document.getChar(lineOffset+1) == '<') {
+			switch (offset - lineOffset) {
+			case 2:
+				if (document.getChar(lineOffset) == '<' && document.getChar(lineOffset + 1) == '<') {
 					return "<<"; //$NON-NLS-1$
 				}
-			}
-			else if (offset - lineOffset == 1) {
+				break;
+			case 1:
 				if (document.getChar(lineOffset) == '<') {
 					return "<"; //$NON-NLS-1$
 				}
-			}
-			else if (offset-lineOffset == 0) {
+				break;
+			case 0:
 				return ""; //$NON-NLS-1$
+			default:
+				break;
 			}
+			return null;
 		}
 		catch (final BadLocationException e) {
+			return null;
 		}
-		return null;
 	}
 	
 	@Override
diff --git a/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/redocs/tex/r/ui/sourceediting/LtxRweaveSourceViewerConfiguration.java b/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/redocs/tex/r/ui/sourceediting/LtxRweaveSourceViewerConfiguration.java
index 3e4efc8..c6d9aa4 100644
--- a/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/redocs/tex/r/ui/sourceediting/LtxRweaveSourceViewerConfiguration.java
+++ b/redocs/org.eclipse.statet.redocs.tex.r/src/org/eclipse/statet/redocs/tex/r/ui/sourceediting/LtxRweaveSourceViewerConfiguration.java
@@ -51,8 +51,8 @@
 import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistCategory;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistProcessor;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IContentAssistComputer;
 import org.eclipse.statet.r.core.IRCoreAccess;
 import org.eclipse.statet.r.core.source.IRDocumentConstants;
 import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
@@ -275,7 +275,7 @@
 	protected void initContentAssist(final ContentAssist assistant) {
 		super.initContentAssist(assistant);
 		
-		final IContentAssistComputer chunkComputer= new RChunkTemplateCompletionComputer();
+		final ContentAssistComputer chunkComputer= new RChunkTemplateCompletionComputer();
 		
 		final ContentAssistProcessor texProcessor= (ContentAssistProcessor) assistant.getContentAssistProcessor(
 				TexDocumentConstants.LTX_DEFAULT_CONTENT_TYPE );
diff --git a/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/internal/redocs/wikitext/r/ui/sourceediting/RChunkTemplateCompletionComputer.java b/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/internal/redocs/wikitext/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
index 712e47e..3c58231 100644
--- a/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/internal/redocs/wikitext/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
+++ b/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/internal/redocs/wikitext/r/ui/sourceediting/RChunkTemplateCompletionComputer.java
@@ -18,14 +18,14 @@
 import java.util.List;
 
 import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
-import org.eclipse.jface.text.templates.DocumentTemplateContext;
 import org.eclipse.jface.text.templates.Template;
 import org.eclipse.jface.text.templates.TemplateContextType;
 import org.eclipse.swt.graphics.Image;
 
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
 import org.eclipse.statet.jcommons.text.core.TextRegion;
 
 import org.eclipse.statet.docmlet.wikitext.core.markup.WikitextMarkupLanguage;
@@ -37,11 +37,13 @@
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.TemplateCompletionComputer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.TemplateProposal;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.TemplateProposal.TemplateProposalParameters;
 import org.eclipse.statet.redocs.r.ui.RedocsRUIResources;
 import org.eclipse.statet.redocs.wikitext.r.core.source.RweaveMarkupLanguage;
 import org.eclipse.statet.redocs.wikitext.r.core.source.WikitextRweaveTemplateContextType;
 
 
+@NonNullByDefault
 public class RChunkTemplateCompletionComputer extends TemplateCompletionComputer {
 	
 	
@@ -56,7 +58,7 @@
 	
 	// sessionStarted not called automatically, because computer is not registered in registry
 	@Override
-	public void sessionStarted(final ISourceEditor editor, final ContentAssist assist) {
+	public void onSessionStarted(final ISourceEditor editor, final ContentAssist assist) {
 		final WikitextMarkupLanguage markupLanguage= MarkupLanguageDocumentSetupParticipant.getMarkupLanguage(
 				editor.getViewer().getDocument(), editor.getDocumentContentInfo().getPartitioning() );
 		if (markupLanguage instanceof RweaveMarkupLanguage) {
@@ -65,40 +67,46 @@
 	}
 	
 	@Override
-	public void sessionEnded() {
+	public void onSessionEnded() {
 		this.markupLanguage= null;
 	}
 	
 	
 	@Override
-	public IStatus computeCompletionProposals(final AssistInvocationContext context, int mode,
+	public void computeCompletionProposals(final AssistInvocationContext context, int mode,
 			final AssistProposalCollector proposals, final IProgressMonitor monitor) {
 		// Set to specific mode to force to include templates in default mode
 		if (mode == COMBINED_MODE) {
 			mode= SPECIFIC_MODE;
 		}
 		
-		sessionStarted(context.getEditor(), null);
+		onSessionStarted(context.getEditor(), null);
 		try {
 			if (this.markupLanguage == null) {
-				return null;
+				return;
 			}
-			return super.computeCompletionProposals(context, mode, proposals, monitor);
+			
+			super.computeCompletionProposals(context, mode, proposals, monitor);
 		}
 		finally {
-			sessionEnded();
+			onSessionEnded();
 		}
 	}
 	
 	@Override
-	protected boolean include(final Template template, final String prefix) {
-		final String pattern= template.getPattern();
-		final int varIdx= pattern.indexOf("${"); //$NON-NLS-1$
-		return ((varIdx < 0 || varIdx >= prefix.length()) && pattern.startsWith(prefix));
+	protected boolean include(final Template template, final TemplateProposalParameters<?> parameters) {
+		final String templatePattern= template.getPattern();
+		final int varIdx= templatePattern.indexOf("${"); //$NON-NLS-1$
+		if ((varIdx < 0 || varIdx >= parameters.namePattern.getPattern().length())
+				&& templatePattern.startsWith(parameters.namePattern.getPattern()) ) {
+			parameters.matchRule= OTHER_MATCH;
+			return true;
+		}
+		return false;
 	}
 	
 	@Override
-	protected String extractPrefix(final AssistInvocationContext context) {
+	protected @Nullable String extractPrefix(final AssistInvocationContext context) {
 		final IDocument document= context.getSourceViewer().getDocument();
 		final int offset= context.getOffset();
 		try {
@@ -117,12 +125,12 @@
 			return ""; //$NON-NLS-1$
 		}
 		catch (final BadLocationException e) {
+			return null;
 		}
-		return null;
 	}
 	
 	@Override
-	protected TemplateContextType getContextType(final AssistInvocationContext context,
+	protected @Nullable TemplateContextType getContextType(final AssistInvocationContext context,
 			final TextRegion region) {
 		return (this.markupLanguage != null) ?
 				getTypeRegistry().getContextType(this.markupLanguage.getName() +
@@ -131,17 +139,16 @@
 	}
 	
 	@Override
-	protected Image getImage(final Template template) {
-		return RedocsRUIResources.INSTANCE.getImage(RedocsRUIResources.OBJ_RCHUNK_IMAGE_ID);
+	protected TemplateProposal createProposal(final TemplateProposalParameters<?> parameters) {
+		if (parameters.matchRule == OTHER_MATCH) {
+			parameters.baseRelevance= 90;
+		}
+		return super.createProposal(parameters);
 	}
 	
 	@Override
-	protected TemplateProposal createProposal(final Template template,
-			final DocumentTemplateContext context, final String prefix, final TextRegion region,
-			int relevance) {
-		relevance= (!prefix.isEmpty() && template.getPattern().startsWith(prefix)) ?
-				95 : 0;
-		return super.createProposal(template, context, prefix, region, relevance);
+	protected Image getImage(final Template template) {
+		return RedocsRUIResources.INSTANCE.getImage(RedocsRUIResources.OBJ_RCHUNK_IMAGE_ID);
 	}
 	
 }
diff --git a/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/redocs/wikitext/r/ui/sourceediting/WikidocRweaveSourceViewerConfiguration.java b/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/redocs/wikitext/r/ui/sourceediting/WikidocRweaveSourceViewerConfiguration.java
index 60672d3..f5ca7f6 100644
--- a/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/redocs/wikitext/r/ui/sourceediting/WikidocRweaveSourceViewerConfiguration.java
+++ b/redocs/org.eclipse.statet.redocs.wikitext.r/src/org/eclipse/statet/redocs/wikitext/r/ui/sourceediting/WikidocRweaveSourceViewerConfiguration.java
@@ -53,8 +53,8 @@
 import org.eclipse.statet.ltk.ui.sourceediting.SourceEditorViewer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssist;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistCategory;
+import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistComputer;
 import org.eclipse.statet.ltk.ui.sourceediting.assist.ContentAssistProcessor;
-import org.eclipse.statet.ltk.ui.sourceediting.assist.IContentAssistComputer;
 import org.eclipse.statet.r.core.IRCoreAccess;
 import org.eclipse.statet.r.core.source.IRDocumentConstants;
 import org.eclipse.statet.r.core.source.RHeuristicTokenScanner;
@@ -291,7 +291,7 @@
 	protected void initContentAssist(final ContentAssist assistant) {
 		super.initContentAssist(assistant);
 		
-		final IContentAssistComputer chunkComputer= new RChunkTemplateCompletionComputer();
+		final ContentAssistComputer chunkComputer= new RChunkTemplateCompletionComputer();
 		
 		final ContentAssistProcessor wikitextProcessor= (ContentAssistProcessor) assistant.getContentAssistProcessor(
 				WikitextDocumentConstants.WIKIDOC_DEFAULT_CONTENT_TYPE );