Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

rewritten: new syntax, much nicer code, simplified, optimized, less t…

…ypos, etc.
  • Loading branch information...
commit 42eef11ef2efd50e8e8c731d92bd21ecd5131681 1 parent c740cf2
Henri authored October 04, 2012

Showing 2 changed files with 76 additions and 121 deletions. Show diff stats Hide diff stats

  1. 6  README.md
  2. 191  completion.opa
6  README.md
Source Rendered
... ...
@@ -1,7 +1,7 @@
1 1
 WCompletionExample
2 2
 ==================
3 3
 
4  
-Example of how to have autocompletion.
  4
+Demo of the Opa autocompletion widget.
5 5
 
6  
-To build:
7  
-opa --parser classic completion.opa
  6
+To build and run:
  7
+opa completion.opa --
191  completion.opa
... ...
@@ -1,120 +1,75 @@
1  
-/* Example of WCompletion use, with all computation on client side */
2  
-
3 1
 import stdlib.widgets.completion
4 2
 import stdlib.io.file
5  
-
6  
-
7  
-/* a dictionnay represent all possible result of the completion */
8  
-type dictionnary = list(string) // a very simple dictionnay implementation, pick something more powerfull if you have big dictionnary
9  
-search_dict(prefix,dict) =
10  
-  match_prefix = String.has_prefix(String.to_lower(prefix),_)
11  
-  List.filter( match_prefix, dict)
12  
-
13  
-string_to_suggestion(s:string):WCompletion.suggestion = {input=s display=<>{s}</> item=s}
14  
-
15  
-@server_private
16  
-dict =  String.explode("\n",  string_of_binary(File.content("english.0")) ) ++ [ "tic","tac","isidor" ]
17  
-// if opa version < 3230 use a smaller dictionnary with nodejs backend, e.g. List.init( _ -> Random.string(8), 1000 )
18  
-
19  
-/* the dictionnay computation is a server operation, could be the db for instance */
20  
-// server_private is equivalent to protected in js-like syntax
21  
-@server_private
22  
-mydictionnary() = dict
23  
-
24  
-/*
25  
-   The central element of the completion widget is the suggest function that is part of the widget configuration.
26  
-   This configuration is almost always constructed on server side.
27  
-   If you take no particular precaution it means that the suggest function of the configuration will come from the server.
28  
-   For both security and efficiency reason, there is no guaranty that the client has the function (space conservation), should have access to the environment of the function (if any) client.
29  
-   It means that the client will have to call the server to execute the suggest function.
30  
-   In the case of the completion widget, it means that any keystroke will triggered a communication between the client and the server.
31  
-   So you need to ensure that the client don't need to call the server to execute the suggest function.
32  
-   We also add the constraint that the client does not possess the dictionnary defining the completion (so it must come from the server).
33  
-   To indicates that the function should be exhanged from the server to the client without needing the server we must authorize the publication of its environment.
34  
-   This is done using @public_env directive.
35  
-*/
36  
-
37  
-
38  
-/* Solution 1
39  
-   The dictionnary will be embedded in the environment of suggest function, so it's content will be fixed at creation of the configuration */
40  
-WithPlainDictionnary = {{
41  
-
42  
-
43  
-  /* simple dictionnay search */
44  
-  suggest_with_dict(prefix, dict) = List.map(string_to_suggestion ,search_dict(prefix,dict))
45  
-
46  
-  /* creating a function to search on a particular dictionnay, the function is suitable for server to client exchange
47  
-     here @public_env ensures the client will have access to anything compute a call to
48  
-     the partial application <suggest_with_dict(_, dict)> on client side
49  
-     This mean the code for suggest_with_dict and the dictionnary can be transfered from the server to the client.
50  
-  */
51  
-  create_suggest(dict) = @public_env(suggest_with_dict(_, dict))
52  
-
53  
-}}
54  
-
55  
-/* Solution 2
56  
-   The suggest function have access to a storage on client side to put the dictionnary in and use it wihtout needing the server.
57  
-   So the dictionnary will be asked only once.
58  
-   It is alse more flexible because you can have different policy to use only a partial dictionnary or evolving one */
59  
-WithCache = {{
60  
-
61  
-
62  
-  // Accessing a dictionnary filtered by a prefix
63  
-  // Note the publish (<=> exposed in js-like syntax) directive, it create an entry point to access mydictionnary from the client
64  
-  // and it stops propagation of the server_private property, => stripped_dictionnary and its caller are not server_private 
65  
-  @publish
66  
-  stripped_dictionnary(prefix) =
67  
-    mydictionnary()
68  
-    |> List.map( String.to_lower, _ )
69  
-    |> search_dict(prefix, _ )
70  
-
71  
-
72  
-  // storage for the client local sub-dictionnary (only valid for the given prefix)
73  
-  @private
74  
-  @client
75  
-  cache_dictionnary = Mutable.make(none : option({prefix:string dict:list(string)}))
76  
-
77  
-  size_to_get_dictionnary = 3 // minimal number of letter to fill the cache
78  
-  size_to_get_completion = size_to_get_dictionnary + 0 // minimal number of letter of letter to have a completion
79  
-
80  
-  // Remark : this function could consider updating the cache if it is older that some long time for dictionnary that change during the interaction
81  
-  /* a suggest function that fills the cache if needed or use it
82  
-     @public_env ensures the function will available on both side */
83  
-  @public_env
84  
-  suggest(prefix:string) =
85  
-    match cache_dictionnary.get()
86  
-    {none} ->
87  
-      do if String.length(prefix) >= size_to_get_dictionnary then cache_dictionnary.set(some({~prefix dict=stripped_dictionnary(prefix)}))
88  
-      if String.length(prefix) >= size_to_get_completion then suggest(prefix)
89  
-      else []
90  
-    {some=cache} ->
91  
-      if String.has_prefix(cache.prefix, prefix) then WithPlainDictionnary.suggest_with_dict(prefix,cache.dict)
92  
-      else do cache_dictionnary.set(none)
93  
-           suggest(prefix)
94  
-
95  
-}}
96  
-
97  
-use_cache = true
98  
-
99  
-// a dummy on_select function, note that the same constraint applies, if we want a pure client on_select computation
100  
-@public_env on_select(_:string) = void
101  
-
102  
-page() =
103  
-  // here we choose one of the solution
104  
-  suggest = if use_cache then /* Solution 1 */ WithCache.suggest
105  
-                         else /* Solution 2 */WithPlainDictionnary.create_suggest(mydictionnary())
106  
-  config = {WCompletion.default_config with ~suggest} // customising the config
107  
-  id = Random.string(8) // a random DOM id
108  
-  // And now the html content, just a completion widget
109  
-  <h1> Completion example </h1>
110  
-  <div>{
111  
-    WCompletion.html(config, on_select, id, {input="" display=<></> item=""} )
112  
-  }</div>
113  
-
114  
-
115  
-do Server.start( Server.http,
116  
-	[
117  
-          {title="Completion example" ~page}
118  
-        ] <: Server.handler
119  
-)
120  
-
  3
+import stdlib.themes.bootstrap
  4
+
  5
+// naive dictionary
  6
+type dictionary = list(string)
  7
+type client_cache = { string prefix, dictionary dict }
  8
+
  9
+function search_dict(prefix,dict) {
  10
+  match_prefix = String.has_prefix(String.to_lower(prefix), _);
  11
+  List.filter(match_prefix, dict)
  12
+}
  13
+
  14
+function string_to_suggestion(string s) {
  15
+  WCompletion.suggestion { input: s, display: <>{s}</>, item: s }
  16
+}
  17
+
  18
+function suggest_with_dict(prefix, dict) {
  19
+  List.map(string_to_suggestion, search_dict(prefix, dict))
  20
+}
  21
+
  22
+protected dict = String.explode("\n", string_of_binary(File.content("english.0")))
  23
+protected function mydictionary() {
  24
+  dict
  25
+}
  26
+
  27
+exposed function stripped_dictionary(prefix) {
  28
+  search_dict(prefix,
  29
+    List.map(String.to_lower, mydictionary())
  30
+  )
  31
+}
  32
+
  33
+// storage for the client local sub-dictionary (only valid for the given prefix)
  34
+client cache_dictionary =
  35
+  Mutable.make(option(client_cache) none)
  36
+
  37
+size_to_get_dictionary = 3
  38
+size_to_get_completion = size_to_get_dictionary + 0 
  39
+
  40
+client function suggest(string prefix) {
  41
+  match (cache_dictionary.get()) {
  42
+    case {none}:
  43
+      if (String.length(prefix) >= size_to_get_dictionary) { 
  44
+        cache_dictionary.set(some({~prefix, dict:stripped_dictionary(prefix)}))
  45
+      }
  46
+      if (String.length(prefix) >= size_to_get_completion) { 
  47
+        suggest(prefix)
  48
+      } else {
  49
+        []
  50
+      }
  51
+    case {some: cache}:
  52
+      if (String.has_prefix(cache.prefix, prefix)) { 
  53
+        suggest_with_dict(prefix, cache.dict)
  54
+      }
  55
+      else {
  56
+        cache_dictionary.set(none);
  57
+        suggest(prefix)
  58
+      }
  59
+  }
  60
+}
  61
+
  62
+client function on_select(_) { void }
  63
+
  64
+function page() {
  65
+  config = { WCompletion.default_config with ~suggest }
  66
+  id = Random.string(8)
  67
+  <div>
  68
+    { WCompletion.html(config, on_select, id, {input:"", display:<></>, item:""}) }
  69
+  </div>
  70
+}
  71
+
  72
+Server.start(
  73
+  Server.http,
  74
+	{ title:"Completion Demo", ~page }
  75
+)

0 notes on commit 42eef11

Please sign in to comment.
Something went wrong with that request. Please try again.