Theme Editor: auto-completion improved:

- reference completion shows all keys defined in current and base files
- support auto-activate for value provider
- do not auto-complete single choices
This commit is contained in:
Karl Tauber
2020-07-10 10:33:10 +02:00
parent c404a0d1a9
commit 6662714277
3 changed files with 129 additions and 47 deletions

View File

@@ -22,10 +22,13 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import org.fife.ui.autocomplete.BasicCompletion;
import org.fife.ui.autocomplete.Completion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.autocomplete.CompletionProviderBase;
@@ -109,10 +112,12 @@ class FlatCompletionProvider
return getValueProvider();
case '$':
case '@':
return getReferenceProvider();
case ' ':
case '\t':
case '#': // colors
return null;
}
}
@@ -126,7 +131,7 @@ class FlatCompletionProvider
private CompletionProvider getKeyProvider() {
if( keyProvider == null )
keyProvider = new KeyCompletionProvider();
keyProvider = KeyCompletionProvider.getInstance();
return keyProvider;
}
@@ -142,20 +147,25 @@ class FlatCompletionProvider
return valueProvider;
}
//---- class KnownKeysCompletionProvider ----------------------------------
//---- class KeyCompletionProvider ----------------------------------------
private static final class KnownKeysCompletionProvider
extends DefaultCompletionProvider
/**
* A completion provider for keys, which always uses all known/predefined keys.
*/
private static final class KeyCompletionProvider
extends BaseCompletionProvider
{
private static KnownKeysCompletionProvider instance;
private static KeyCompletionProvider instance;
static KnownKeysCompletionProvider getInstance() {
static KeyCompletionProvider getInstance() {
if( instance == null )
instance = new KnownKeysCompletionProvider();
instance = new KeyCompletionProvider();
return instance;
}
KnownKeysCompletionProvider() {
KeyCompletionProvider() {
setAutoActivationRules( true, "." );
// load all keys
HashSet<String> keys = new HashSet<>();
try {
@@ -197,19 +207,14 @@ class FlatCompletionProvider
}
}
//---- class KeyCompletionProvider ----------------------------------------
//---- class BaseCompletionProvider ---------------------------------------
private static class KeyCompletionProvider
//TODO remove if https://github.com/bobbylight/AutoComplete/issues/77 is fixed
private static class BaseCompletionProvider
extends DefaultCompletionProvider
{
KeyCompletionProvider() {
setParent( KnownKeysCompletionProvider.getInstance() );
}
@Override
protected boolean isValidChar( char ch ) {
return super.isValidChar( ch ) || ch == '.';
}
private boolean autoActivateAfterLetters;
private String autoActivateChars;
@Override
public boolean isAutoActivateOkay( JTextComponent comp ) {
@@ -219,35 +224,89 @@ class FlatCompletionProvider
try {
char ch = comp.getText( caretPosition - 1, 1 ).charAt( 0 );
return isAutoActivateOkay( ch );
return (autoActivateAfterLetters && Character.isLetter( ch )) ||
(autoActivateChars != null && autoActivateChars.indexOf( ch ) >= 0);
} catch( BadLocationException | IndexOutOfBoundsException ex ) {
// ignore
return false;
}
}
protected boolean isAutoActivateOkay( char ch ) {
return Character.isLetter( ch ) || ch == '.';
@Override
public void setAutoActivationRules( boolean letters, String others ) {
autoActivateAfterLetters = letters;
autoActivateChars = others;
}
}
//---- class ReferenceCompletionProvider ----------------------------------
/**
* A completion provider for references within values. Only keys defined
* in current properties file and in base properties files are used.
*/
private static class ReferenceCompletionProvider
extends KeyCompletionProvider
extends BaseCompletionProvider
{
private Set<String> lastKeys;
ReferenceCompletionProvider() {
setAutoActivationRules( true, "$@." );
}
@Override
protected boolean isAutoActivateOkay( char ch ) {
return ch == '$' || super.isAutoActivateOkay( ch );
protected boolean isValidChar( char ch ) {
return super.isValidChar( ch ) || ch == '.' || ch == '$' || ch == '@';
}
@Override
protected List<Completion> getCompletionsImpl( JTextComponent comp ) {
updateCompletions( comp );
return super.getCompletionsImpl( comp );
}
@Override
public List<Completion> getCompletionsAt( JTextComponent comp, Point pt ) {
updateCompletions( comp );
return super.getCompletionsAt( comp, pt );
}
@Override
public List<ParameterizedCompletion> getParameterizedCompletions( JTextComponent comp ) {
updateCompletions( comp );
return super.getParameterizedCompletions( comp );
}
private void updateCompletions( JTextComponent comp ) {
FlatSyntaxTextArea fsta = (FlatSyntaxTextArea) comp;
Set<String> keys = fsta.propertiesSupport.getAllKeys();
if( keys == lastKeys )
return;
completions.clear();
for( String key : keys ) {
if( key.startsWith( "*." ) )
continue;
if( !key.startsWith( "@" ) )
key = "$".concat( key );
completions.add( new BasicCompletion( this, key ) );
}
Collections.sort(completions);
}
}
//---- class ValueCompletionProvider --------------------------------------
/**
* A completion provider for values.
*/
private static class ValueCompletionProvider
extends DefaultCompletionProvider
extends BaseCompletionProvider
{
ValueCompletionProvider() {
setAutoActivationRules( true, null );
setParameterizedCompletionParams( '(', ",", ')' );
addFunction( "rgb",
@@ -293,17 +352,12 @@ class FlatCompletionProvider
FunctionCompletion f = new FunctionCompletion( this, name, null ) {
@Override
public String toString() {
return getDefinitionString().replace( "(", " (" );
return getDefinitionString().replace( "(", " (" ).replace( ",", ", " );
}
};
f.setParams( params );
addCompletion( f );
}
@Override
public boolean isAutoActivateOkay( JTextComponent tc ) {
return false;
}
}
}

View File

@@ -21,6 +21,7 @@ import java.awt.Color;
import java.awt.Font;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import javax.swing.JLayer;
@@ -63,8 +64,8 @@ class FlatThemeEditorPane
// textArea.setUseColorOfColorTokens( true );
// theme
try {
Theme theme = Theme.load( getClass().getResourceAsStream( "light.xml" ) );
try( InputStream in = getClass().getResourceAsStream( "light.xml" ) ) {
Theme theme = Theme.load( in );
theme.apply( textArea );
} catch( IOException ex ) {
ex.printStackTrace();
@@ -79,6 +80,7 @@ class FlatThemeEditorPane
// autocomplete
CompletionProvider provider = new FlatCompletionProvider();
AutoCompletion ac = new AutoCompletion( provider );
ac.setAutoCompleteSingleChoices( false );
ac.setAutoActivationEnabled( true );
ac.setParameterAssistanceEnabled( true );
ac.setChoicesWindowSize( UIScale.scale( 300 ), UIScale.scale( 400 ) );

View File

@@ -23,9 +23,11 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Function;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
@@ -50,6 +52,8 @@ class FlatThemePropertiesSupport
private long[] baseFilesLastModified;
private Properties[] basePropertiesCache;
private Set<String> allKeysCache;
FlatThemePropertiesSupport( FlatSyntaxTextArea textArea ) {
this.textArea = textArea;
@@ -129,19 +133,7 @@ class FlatThemePropertiesSupport
// look in base properties files
for( int i = 0; i < baseFiles.length; i++ ) {
long lastModified = baseFiles[i].lastModified();
if( baseFilesLastModified[i] != lastModified ) {
// (re)load base properties file
baseFilesLastModified[i] = lastModified;
basePropertiesCache[i] = new Properties();
try( InputStream in = new FileInputStream( baseFiles[i] ) ) {
basePropertiesCache[i].load( in );
} catch( IOException ex ) {
ex.printStackTrace(); //TODO
}
}
value = basePropertiesCache[i].getProperty( key );
value = getBaseProperties( i ).getProperty( key );
if( value != null )
return value;
}
@@ -162,9 +154,43 @@ class FlatThemePropertiesSupport
return propertiesCache;
}
private Properties getBaseProperties( int index ) {
long lastModified = baseFiles[index].lastModified();
if( baseFilesLastModified[index] != lastModified || basePropertiesCache[index] == null ) {
// (re)load base properties file
baseFilesLastModified[index] = lastModified;
basePropertiesCache[index] = new Properties();
try( InputStream in = new FileInputStream( baseFiles[index] ) ) {
basePropertiesCache[index].load( in );
} catch( IOException ex ) {
ex.printStackTrace(); //TODO
}
}
return basePropertiesCache[index];
}
Set<String> getAllKeys() {
if( allKeysCache != null )
return allKeysCache;
allKeysCache = new HashSet<>();
for( Object key : getProperties().keySet() )
allKeysCache.add( (String) key );
for( int i = 0; i < baseFiles.length; i++ ) {
for( Object key : getBaseProperties( i ).keySet() )
allKeysCache.add( (String) key );
}
return allKeysCache;
}
private void clearCache() {
propertiesCache = null;
parsedValueCache.clear();
allKeysCache = null;
}
//---- interface DocumentListener ----