!7 sclass SimpleSnippetSearch extends DynModule { InputChooser inputChooser = new(this).setDelay(1000); bool fullTextSearch; // look at snippet texts too bool searchInBackground; // search when window is hidden transient int maxInputLength = 120; transient int maxResults = 1000; transient S query; transient L result; transient WaitForStableValue waitForStableValue = new(1.0); transient volatile bool calculating; transient JTable table; transient ReliableSingleThread rst; transient SimpleLiveValue lvQuery = stringLiveValue(); transient SimpleLiveValue lvResultsTitle = stringLiveValue(); visualize { SimpleLiveValue lvFullTextSearch = dm_fieldLiveValue('fullTextSearch); lvFullTextSearch.onChange(r forceNewSearch); ret northAndCenter(wrap(inputChooser), withRightAlignedButtons( jLiveValueSection(lvResultsTitle, dataToTable_uneditable(result, table = sexyTable())), jLiveValueCheckBox("Full Text Search", lvFullTextSearch)); } S switchableFields() { ret 'searchInBackground; } void forceNewSearch { setField(query := null); } start { lvQuery.set(query); doEvery(500, rstUpdate()); } void update { if (inputChooser.update()) calc(inputChooser.input()); } void calc(S q) { if (!allowedToSearch || eq(query, q) || empty(query)) ret; query = q; result = null; lvQuery.set(q); tempSetField(this, calculating := true); tbSearch_quick.set(!fullTextSearch); tbSearch_html.set("want"); // We want the HTML too L l = tbSearch(maxResults, q); S html = tbSearch_html!; S desc = regexpExtractIC("Found (\\d+\\+?) snippets?", html); lvResultsTitle.set(or(desc, "No snippet results") + " for " + quote(q) + (fullTextSearch ? " (full text search)" : "")); result = map(l, func(Snippet s) -> SS { litorderedmap("ID + Title" := snippetWithTitle(s) /*, "Type" := s.type*/ )}); JTable t = table; if (t != null) dataToTable_uneditable(result, t); } bool allowedToSearch() enter { ret searchInBackground || dm_isVisible(); } }