Skip to content

Dynamically update word completions #1491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 57 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
7dbf009
Start handling insert_text signal instead of key press signal in word…
colinkiama Nov 2, 2024
46cf5d4
Merge remote-tracking branch 'origin/master' into 851-fix-word-comple…
colinkiama Nov 2, 2024
ec2cc9c
Word completion now adds words when multiple characters of text are a…
colinkiama Nov 2, 2024
79e0429
Bring back handling of words from text inserted that isn't at a word …
colinkiama Nov 2, 2024
8a6edf0
Prepare for prefix tree word removal in word completion engine
colinkiama Nov 10, 2024
27d585f
word-completion: Add "remove" method to PrefixTree
colinkiama Nov 21, 2024
ab7fa75
Detect when text gets inserted between a word
colinkiama Nov 22, 2024
f748c01
word-completion: Support text being inserted between a word
colinkiama Nov 24, 2024
ddd0260
Fix warning on startup
Nov 25, 2024
744a0f6
Fix warning on rename
Nov 25, 2024
a1ce387
Merge branch 'master' into 851-fix-word-completion-logic
jeremypw Nov 26, 2024
01af591
Fix minor code style
Nov 26, 2024
12100cb
Continue to handle insertions even if no current completions
Nov 26, 2024
3654d60
Do not show current word to find in completion list
Nov 26, 2024
6176df7
Ensure PrefixNode value is immutable
Nov 26, 2024
7a5d7a1
Move stuff from engine.vala to plugin.vala, rework.
Nov 27, 2024
8bdba91
More reworking
Nov 28, 2024
fca35e5
Reimplement threaded parse, use string not text iters
Nov 28, 2024
d18f662
Move some code into parser engine; remove unneeded code
Nov 28, 2024
e3e68ba
Split out PrefixNode into separate file; renaming and cleanup
Nov 28, 2024
e742e07
Start to fix update after deletions
Nov 28, 2024
f0199fa
Use same delimiters in completionprovider; cleanup
Nov 29, 2024
8670ef3
Rework getting completions
Nov 29, 2024
9548377
Reword plugin.after_delete_range; Use fewer iters; Use own delimiters
Nov 29, 2024
03d01e2
Fix initial parsing
Nov 29, 2024
b23d9c7
Different min lengths for stored words and prefixes to trigger comple…
Nov 29, 2024
982cf6a
Handle delimiters inserted after word
Nov 29, 2024
3c2c42a
Rework completionprovider - overwrite remainder of word
Nov 29, 2024
edee393
Fix delete middle of words
Nov 29, 2024
23ab6a6
Fix parsing into words
Nov 30, 2024
c9f14f0
Fix insertion and deletion
Nov 30, 2024
5dcac67
Move some code back to engine; move some code into PrefixNode
Nov 30, 2024
4b1ffbf
Fix deleting single words
Nov 30, 2024
178724c
Fix finding words in string
Dec 1, 2024
307774b
Incorporate changes from experiments
Dec 3, 2024
f0d0dd6
Cleanup, comment, fix find end node
Dec 3, 2024
f343fda
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Dec 4, 2024
f89613d
Lose unnecessary func
Dec 4, 2024
699dbf6
Fix regression - allow find word end char
Dec 5, 2024
4296f43
Show completions after delete one letter
Dec 5, 2024
2341656
Fix regression - do not show deleted but unreaped words
Dec 5, 2024
f704619
Code style, cleanup
Dec 5, 2024
1298efa
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Dec 16, 2024
4969019
Merge branch 'master' into jeremypw/silence-some-warnings
jeremypw Dec 18, 2024
9d3a788
Merge branch 'jeremypw/silence-some-warnings' into jeremypw/word-comp…
jeremypw Dec 18, 2024
d776da6
Limit total number of completions
jeremypw Dec 18, 2024
232dfcd
Do not connect signals until initial parsing complete
jeremypw Dec 18, 2024
51794aa
Rework getting immediate prev and next words
jeremypw Dec 18, 2024
0cc6679
Fix delete range
jeremypw Dec 18, 2024
4c2d536
Connect and disconnect signals correctly
jeremypw Dec 20, 2024
d0fb053
Throttle completions when deleting
jeremypw Dec 20, 2024
f9749fe
Aways connect signals
jeremypw Dec 20, 2024
a7f1b8f
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Jan 12, 2025
72e6a31
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Jan 21, 2025
d27dd85
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Jan 29, 2025
3b0829b
Merge branch 'master' into jeremypw/word-completion-rework
jeremypw Jan 29, 2025
c158126
Merge branch 'master' into jeremypw/word-completion-rework
Feb 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 78 additions & 60 deletions plugins/word-completion/completion-provider.vala
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright 2024 elementary, Inc. <https://elementary.io>
* Copyright (c) 2013 Mario Guerriero <[email protected]>
*
* This is a free software; you can redistribute it and/or
Expand All @@ -18,94 +19,109 @@
*
*/

public class Scratch.Plugins.CompletionProvider : Gtk.SourceCompletionProvider, Object {
public string name;
public int priority;
public class Scratch.Plugins.CompletionProvider : Gtk.SourceCompletionProvider, GLib.Object {
private const int MAX_COMPLETIONS = 10;
public string name { get; construct; }
public int priority { get; construct; }
public int interactive_delay { get; construct; }
public Gtk.SourceCompletionActivation activation { get; construct; }

public const string COMPLETION_END_MARK_NAME = "ScratchWordCompletionEnd";
public const string COMPLETION_START_MARK_NAME = "ScratchWordCompletionStart";

private Gtk.TextView? view;
private Gtk.TextBuffer? buffer;
private Euclide.Completion.Parser parser;
public Gtk.TextView? view { get; construct; }
public Euclide.Completion.Parser parser { get; construct; }

private unowned Gtk.TextBuffer buffer {
get {
return view.buffer;
}
}

private Gtk.TextMark completion_end_mark;
private Gtk.TextMark completion_start_mark;
private string current_text_to_find = "";

public signal void can_propose (bool b);

public CompletionProvider (Scratch.Plugins.Completion completion) {
this.view = completion.current_view as Gtk.TextView;
this.buffer = completion.current_view.buffer;
this.parser = completion.parser;
public CompletionProvider (
Euclide.Completion.Parser _parser,
Scratch.Services.Document _doc
) {

Object (
parser: _parser,
view: _doc.source_view,
name: _("%s - Word Completion").printf (_doc.get_basename ())
);
}

construct {
interactive_delay = (int) Completion.INTERACTIVE_DELAY;
activation = INTERACTIVE | USER_REQUESTED;
Gtk.TextIter iter;
buffer.get_iter_at_offset (out iter, 0);
view.buffer.get_iter_at_offset (out iter, 0);
completion_end_mark = buffer.create_mark (COMPLETION_END_MARK_NAME, iter, false);
completion_start_mark = buffer.create_mark (COMPLETION_START_MARK_NAME, iter, false);
}

public string get_name () {
return this.name;
}
public override bool match (Gtk.SourceCompletionContext context) {
var iter = context.iter;
var start_iter = iter;
var end_iter = iter;
bool found = false;

public int get_priority () {
return this.priority;
}

public bool match (Gtk.SourceCompletionContext context) {
Gtk.TextIter start, end;
buffer.get_iter_at_offset (out end, buffer.cursor_position);
start = end.copy ();
Euclide.Completion.Parser.back_to_word_start (ref start);
string text = buffer.get_text (start, end, true);
var preceding_word = parser.get_word_immediately_before (iter);
if (preceding_word != "") {
found = parser.match (preceding_word);
current_text_to_find = found ? preceding_word : "";
}

return parser.match (text);
return found;
}

public void populate (Gtk.SourceCompletionContext context) {
public override void populate (Gtk.SourceCompletionContext context) {
/*Store current insertion point for use in activate_proposal */
GLib.List<Gtk.SourceCompletionItem>? file_props;
bool no_minimum = (context.get_activation () == Gtk.SourceCompletionActivation.USER_REQUESTED);
get_proposals (out file_props, no_minimum);
context.add_proposals (this, file_props, true);
}

public bool activate_proposal (Gtk.SourceCompletionProposal proposal, Gtk.TextIter iter) {
public override bool activate_proposal (Gtk.SourceCompletionProposal proposal, Gtk.TextIter iter) {
Gtk.TextIter start;
Gtk.TextIter end;
Gtk.TextIter end_iter;
Gtk.TextMark mark;

mark = buffer.get_mark (COMPLETION_END_MARK_NAME);
buffer.get_iter_at_mark (out end, mark);
buffer.get_iter_at_mark (out end_iter, mark);
var end_pos = end_iter.get_offset ();
// If inserting in middle of word then completion overwrites end of word
var following_word = parser.get_word_immediately_after (end_iter);
if (following_word != "") {
buffer.get_iter_at_offset (out end_iter, end_pos + following_word.length);
}

mark = buffer.get_mark (COMPLETION_START_MARK_NAME);
buffer.get_iter_at_mark (out start, mark);

buffer.@delete (ref start, ref end);
buffer.@delete (ref start, ref end_iter);
buffer.insert (ref start, proposal.get_text (), proposal.get_text ().length);
return true;
}

public Gtk.SourceCompletionActivation get_activation () {
return Gtk.SourceCompletionActivation.INTERACTIVE |
Gtk.SourceCompletionActivation.USER_REQUESTED;
}

public int get_interactive_delay () {
return 0;
}

public bool get_start_iter (Gtk.SourceCompletionContext context,
public override bool get_start_iter (Gtk.SourceCompletionContext context,
Gtk.SourceCompletionProposal proposal,
out Gtk.TextIter iter) {
var mark = buffer.get_insert ();
Gtk.TextIter cursor_iter;
buffer.get_iter_at_mark (out cursor_iter, mark);

iter = cursor_iter;
Euclide.Completion.Parser.back_to_word_start (ref iter);
var word_start = buffer.cursor_position;
buffer.get_iter_at_offset (out iter, word_start);
return true;
}

public override string get_name () {
return name;
}

private bool get_proposals (out GLib.List<Gtk.SourceCompletionItem>? props, bool no_minimum) {
string to_find = "";
Expand All @@ -114,36 +130,38 @@ public class Scratch.Plugins.CompletionProvider : Gtk.SourceCompletionProvider,

Gtk.TextIter start, end;
buffer.get_selection_bounds (out start, out end);

to_find = temp_buffer.get_text (start, end, true);

if (to_find.length == 0) {
temp_buffer.get_iter_at_offset (out end, buffer.cursor_position);

start = end;
Euclide.Completion.Parser.back_to_word_start (ref start);

to_find = buffer.get_text (start, end, false);
to_find = current_text_to_find;
}

buffer.move_mark_by_name (COMPLETION_END_MARK_NAME, end);
buffer.move_mark_by_name (COMPLETION_START_MARK_NAME, start);

/* There is no minimum length of word to find if the user requested a completion */
if (no_minimum || to_find.length >= Euclide.Completion.Parser.MINIMUM_WORD_LENGTH) {
if (no_minimum || to_find.length >= Euclide.Completion.Parser.MINIMUM_PREFIX_LENGTH) {
/* Get proposals, if any */
List<string> prop_word_list;
if (parser.get_for_word (to_find, out prop_word_list)) {
foreach (var word in prop_word_list) {
var item = new Gtk.SourceCompletionItem ();
item.label = word;
item.text = word;
props.append (item);
var completions = parser.get_current_completions (to_find);
if (completions.length () > 0) {
var index = 0;
foreach (var completion in completions) {
if (completion.length > 0) {
var item = new Gtk.SourceCompletionItem ();
var word = to_find + completion;
item.label = word;
item.text = completion;
props.append (item);
if (++index > MAX_COMPLETIONS) {
break;
}
}
}

return true;
}
}

return false;
}
}
Loading
Loading