summary refs log tree commit diff stats
path: root/compiler/prefixmatches.nim
diff options
context:
space:
mode:
authorAndreas Rumpf <rumpf_a@web.de>2017-03-07 10:58:43 +0100
committerAndreas Rumpf <rumpf_a@web.de>2017-03-07 10:58:43 +0100
commit9ed322323ee6f396595aa93aa6c244b3a5637d50 (patch)
tree1d748968a699e359f11b6b5513476f5f13dc6f89 /compiler/prefixmatches.nim
parentd9ad6465af0aad25c4dba928f0cc9499cff6017b (diff)
downloadNim-9ed322323ee6f396595aa93aa6c244b3a5637d50.tar.gz
nimsuggest: better ordering of symbols (part 1)
Diffstat (limited to 'compiler/prefixmatches.nim')
-rw-r--r--compiler/prefixmatches.nim86
1 files changed, 86 insertions, 0 deletions
diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim
new file mode 100644
index 000000000..2630225de
--- /dev/null
+++ b/compiler/prefixmatches.nim
@@ -0,0 +1,86 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2017 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+from strutils import toLowerAscii
+
+type
+  PrefixMatch* {.pure.} = enum
+    None,   ## no prefix detected
+    Prefix, ## prefix does match the symbol
+    Substr, ## prefix is a substring of the symbol
+    Abbrev  ## prefix is an abbreviation of the symbol
+
+proc prefixMatch*(p, s: string): PrefixMatch =
+  template eq(a, b): bool = a.toLowerAscii == b.toLowerAscii
+  if p.len > s.len: return PrefixMatch.None
+  var i = 0
+  let L = s.len
+  # check for prefix/contains:
+  while i < L:
+    if s[i] == '_': inc i
+    if eq(s[i], p[0]):
+      var ii = i+1
+      var jj = 1
+      while ii < L and jj < p.len:
+        if p[jj] == '_': inc jj
+        if s[ii] == '_': inc ii
+        if not eq(s[ii], p[jj]): break
+        inc ii
+        inc jj
+
+      if jj >= p.len:
+        if i == 0: return PrefixMatch.Prefix
+        else: return PrefixMatch.Substr
+    inc i
+  # check for abbrev:
+  if eq(s[0], p[0]):
+    i = 1
+    var j = 1
+    while i < s.len:
+      if s[i] == '_' and i < s.len-1:
+        if j < p.len and eq(p[j], s[i+1]): inc j
+        else: return PrefixMatch.None
+      if s[i] in {'A'..'Z'} and s[i-1] notin {'A'..'Z'}:
+        if j < p.len and eq(p[j], s[i]): inc j
+        else: return PrefixMatch.None
+      inc i
+    return PrefixMatch.Abbrev
+  return PrefixMatch.None
+
+when isMainModule:
+  import macros
+
+  macro check(val, body: untyped): untyped =
+    result = newStmtList()
+    expectKind body, nnkStmtList
+    for b in body:
+      expectKind b, nnkPar
+      expectLen b, 2
+      let p = b[0]
+      let s = b[1]
+      result.add quote do:
+        echo prefixMatch(`p`, `s`) == `val`
+
+  check PrefixMatch.Prefix:
+    ("abc", "abc")
+    ("a", "abc")
+    ("xyz", "X_yzzzZe")
+
+  check PrefixMatch.Substr:
+    ("b", "abc")
+    ("abc", "fooabcabc")
+    ("abC", "foo_AB_c")
+
+  check PrefixMatch.Abbrev:
+    ("abc", "AxxxBxxxCxxx")
+    ("xyz", "X_yabcZe")
+
+  check PrefixMatch.None:
+    ("foobar", "afkslfjd_as")
+    ("xyz", "X_yuuZuuZe")