Skip to content

Commit

Permalink
Add smart auto-completions
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonPieper authored Jun 2, 2024
1 parent f8e9137 commit 4993782
Show file tree
Hide file tree
Showing 4 changed files with 249 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
Expand Down Expand Up @@ -47,12 +48,15 @@
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
Expand All @@ -73,7 +77,7 @@

public class MainActivity
extends AppCompatActivity
implements ShaderEditor.OnTextChangedListener {
implements ShaderEditor.OnTextChangedListener, ShaderEditor.CodeCompletionListener {
private static final String SELECTED_SHADER = "selected_shader";
private static final String CODE_VISIBLE = "code_visible";
private static final int PREVIEW_SHADER = 1;
Expand Down Expand Up @@ -105,6 +109,8 @@ public void run() {
private volatile int fps;
private float[] qualityValues;
private float quality = 1f;
private final List<String> currentCompletions = new ArrayList<>();
private Adapter completionsAdapter;

@Override
public void onConfigurationChanged(@NonNull Configuration newConfig) {
Expand Down Expand Up @@ -139,6 +145,12 @@ public void onTextChanged(String text) {
setFragmentShader(text);
}

@Override
public void onCodeCompletions(@NonNull List<String> completions, int position) {
completionsAdapter.setPosition(position);
completionsAdapter.submitList(completions);
}

@Override
protected void onActivityResult(
int requestCode,
Expand Down Expand Up @@ -296,19 +308,8 @@ private void initExtraKeys() {
RecyclerView completions = extraKeys.findViewById(R.id.completions);
completions.setLayoutManager(new LinearLayoutManager(this, RecyclerView.HORIZONTAL,
false));
completions.setAdapter(new Adapter(this,
Arrays.asList(
"if",
"else",
"for",
"while",
"texture2D",
"distance",
"smoothstep",
"min",
"max"
)
));
this.completionsAdapter = new Adapter(this);
completions.setAdapter(completionsAdapter);
DividerItemDecoration divider = new DividerItemDecoration(completions.getContext(),
DividerItemDecoration.HORIZONTAL);
divider.setDrawable(Objects.requireNonNull(ContextCompat.getDrawable(this,
Expand Down Expand Up @@ -347,30 +348,47 @@ public void onGlobalLayout() {
: View.GONE);
}

private class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
private final List<String> list;
private static final DiffUtil.ItemCallback<String> DIFF_CALLBACK =
new DiffUtil.ItemCallback<String>() {
@Override
public boolean areItemsTheSame(@NonNull String oldItem, @NonNull String newItem) {
// Update the condition according to your unique identifier
return oldItem.equals(newItem);
}

@Override
public boolean areContentsTheSame(@NonNull String oldItem,
@NonNull String newItem) {
// Return true if the contents of the items have not changed
return oldItem.equals(newItem);
}
};

private class Adapter extends ListAdapter<String, Adapter.ViewHolder> {
@NonNull
private final LayoutInflater inflater;
int position = 0;

public Adapter(Context context, List<String> list) {
public Adapter(Context context) {
super(DIFF_CALLBACK);
this.inflater = LayoutInflater.from(context);
this.list = list;
}

@NonNull
@Override
public Adapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = inflater.inflate(R.layout.extra_key_btn, parent, false);
return new ViewHolder(view);
}

@Override
public void onBindViewHolder(@NonNull Adapter.ViewHolder holder, int position) {
holder.update(list.get(position));
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
String item = getItem(position); // Use getItem provided by ListAdapter
holder.update(item);
}

@Override
public int getItemCount() {
return list.size();
public void setPosition(int position) {
this.position = position;
}

private class ViewHolder extends RecyclerView.ViewHolder {
Expand All @@ -379,7 +397,10 @@ private class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(@NonNull View itemView) {
super(itemView);
btn = itemView.findViewById(R.id.btn);
btn.setOnClickListener((v) -> editorFragment.insert(btn.getText()));
btn.setOnClickListener((v) -> {
CharSequence text = btn.getText();
editorFragment.insert(text.subSequence(position, text.length()));
});
itemView.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ public View onCreateView(
if (activity instanceof ShaderEditor.OnTextChangedListener) {
shaderEditor.setOnTextChangedListener(
(ShaderEditor.OnTextChangedListener) activity);
shaderEditor.setOnCompletionsListener(
(ShaderEditor.CodeCompletionListener) activity);
} else {
throw new ClassCastException(activity +
" must implement " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
import java.util.List;

public class Lexer implements Iterable<Token> {
public String source(Token token) {
return source.substring(token.startOffset(), token.endOffset());
@NonNull
public static CharSequence tokenSource(@NonNull Token token, @NonNull CharSequence source) {
return source.subSequence(token.startOffset(), token.endOffset());
}

public static class Diff {
Expand Down Expand Up @@ -599,15 +600,50 @@ private int skipWhitespace() {
return possibleOctal && incorrectOctal ? TokenType.INVALID : type;
}

public static List<String> complete(@NonNull String text, @NonNull Token.Category type) {
public static List<String> completeKeyword(@NonNull String text, @NonNull Token.Category type) {
List<String> result = new ArrayList<>();
TrieNode root = tokenRoot(type);
if (root != null) {
root.findAll(text, (short) TokenType.INVALID.ordinal(), result);
}

if (type == Token.Category.PREPROC) {
// Also add normal keywords to the list
KEYWORDS_TRIE.findAll(text, (short) TokenType.INVALID.ordinal(), result);
}

return result;
}

/**
* Performs a binary search to find the token that includes the given position.
* Assumes tokens are non-overlapping and touch each other.
*
* @param tokens List of tokens sorted by their start offsets.
* @param position The offset to search for.
* @return The token that contains the position, or null if no such token exists.
*/
@Nullable
public static Token findToken(@NonNull List<Token> tokens, int position) {
int low = 0;
int high = tokens.size() - 1;

while (low <= high) {
int mid = low + (high - low) / 2;
Token midToken = tokens.get(mid);

if (position >= midToken.startOffset() && position <= midToken.endOffset()) {
return midToken;
} else if (position < midToken.startOffset()) {
high = mid - 1;
} else {
low = mid + 1;
}
}

return null; // No token contains the position
}

@Nullable
private static TrieNode tokenRoot(@NonNull Token.Category type) {
switch (type) {
Expand Down
Loading

0 comments on commit 4993782

Please sign in to comment.