diff --git a/app/src/main/java/de/kuschku/quasseldroid/settings/AutoCompleteSettings.kt b/app/src/main/java/de/kuschku/quasseldroid/settings/AutoCompleteSettings.kt
index a195661f05bd0def5241e3cadea91645b0649871..110ce5a63854fc3afdd3f6ea246c5a7540bfb90a 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/settings/AutoCompleteSettings.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/settings/AutoCompleteSettings.kt
@@ -24,7 +24,10 @@ data class AutoCompleteSettings(
   val button: Boolean = false,
   val doubleTap: Boolean = true,
   val auto: Boolean = false,
-  val prefix: Boolean = true
+  val prefix: Boolean = true,
+  val nicks: Boolean = true,
+  val buffers: Boolean = true,
+  val aliases: Boolean = true
 ) {
   companion object {
     val DEFAULT = AutoCompleteSettings()
diff --git a/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt b/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
index 015d621ba63d7ec3b26a128efc808fbed9b2565d..fc6e0773edddfa14271fab0aec3aa55f7e0542ff 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/settings/Settings.kt
@@ -184,6 +184,18 @@ object Settings {
       prefix = getBoolean(
         context.getString(R.string.preference_autocomplete_prefix_key),
         AutoCompleteSettings.DEFAULT.prefix
+      ),
+      nicks = getBoolean(
+        context.getString(R.string.preference_autocomplete_nicks_key),
+        AutoCompleteSettings.DEFAULT.nicks
+      ),
+      buffers = getBoolean(
+        context.getString(R.string.preference_autocomplete_buffers_key),
+        AutoCompleteSettings.DEFAULT.buffers
+      ),
+      aliases = getBoolean(
+        context.getString(R.string.preference_autocomplete_aliases_key),
+        AutoCompleteSettings.DEFAULT.aliases
       )
     )
   }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
index 3f33ead50e1e985063e5252dc53e07ed614d43f1..a8e61b0a025558e0b618bc9f09a0c8d6ff6f98e9 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/info/user/UserInfoFragment.kt
@@ -41,7 +41,6 @@ import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.ChatActivity
-import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper.Companion.IGNORED_CHARS
 import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.*
 import de.kuschku.quasseldroid.util.irc.format.ContentFormatter
@@ -50,6 +49,7 @@ import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
 import de.kuschku.quasseldroid.util.ui.BetterLinkMovementMethod
 import de.kuschku.quasseldroid.util.ui.LinkLongClickMenuHelper
 import de.kuschku.quasseldroid.util.ui.TextDrawable
+import de.kuschku.quasseldroid.viewmodel.EditorViewModel.Companion.IGNORED_CHARS
 import io.reactivex.Observable
 import javax.inject.Inject
 
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
index 5da440f8e7fbd7d5a20e35adf944c8a92f2f3ab3..7bb373c4885aa4b95ae12dfd9d76c30d19dda990 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteAdapter.kt
@@ -76,6 +76,11 @@ class AutoCompleteAdapter @Inject constructor(
 
       holder
     }
+    VIEWTYPE_ALIAS                           -> AutoCompleteViewHolder.AliasViewHolder(
+      LayoutInflater.from(parent.context)
+        .inflate(R.layout.widget_alias, parent, false),
+      clickListener = clickListener
+    )
     else                                     -> throw IllegalArgumentException(
       "Invoked with wrong item type"
     )
@@ -87,6 +92,7 @@ class AutoCompleteAdapter @Inject constructor(
   override fun getItemViewType(position: Int) = getItem(position).let {
     when {
       it is AutoCompleteItem.ChannelItem         -> VIEWTYPE_CHANNEL
+      it is AutoCompleteItem.AliasItem           -> VIEWTYPE_ALIAS
       it is AutoCompleteItem.UserItem && it.away -> VIEWTYPE_NICK_AWAY
       else                                       -> VIEWTYPE_NICK_ACTIVE
     }
@@ -96,6 +102,7 @@ class AutoCompleteAdapter @Inject constructor(
     fun bind(data: AutoCompleteItem) = when {
       data is AutoCompleteItem.UserItem && this is NickViewHolder       -> this.bindImpl(data)
       data is AutoCompleteItem.ChannelItem && this is ChannelViewHolder -> this.bindImpl(data)
+      data is AutoCompleteItem.AliasItem && this is AliasViewHolder     -> this.bindImpl(data)
       else                                                              -> throw IllegalArgumentException(
         "Invoked with wrong item type"
       )
@@ -188,11 +195,41 @@ class AutoCompleteAdapter @Inject constructor(
         )
       }
     }
+
+    class AliasViewHolder(
+      itemView: View,
+      private val clickListener: ((String) -> Unit)? = null
+    ) : AutoCompleteViewHolder(itemView) {
+      @BindView(R.id.alias)
+      lateinit var alias: TextView
+
+      @BindView(R.id.expansion)
+      lateinit var expansion: TextView
+
+      var value: String? = null
+
+      init {
+        ButterKnife.bind(this, itemView)
+        itemView.setOnClickListener {
+          val value = value
+          if (value != null)
+            clickListener?.invoke(value)
+        }
+      }
+
+      fun bindImpl(data: AutoCompleteItem.AliasItem) {
+        value = data.name
+
+        alias.text = data.alias
+        expansion.text = data.expansion
+      }
+    }
   }
 
   companion object {
     const val VIEWTYPE_CHANNEL = 0
     const val VIEWTYPE_NICK_ACTIVE = 1
     const val VIEWTYPE_NICK_AWAY = 2
+    const val VIEWTYPE_ALIAS = 3
   }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
index 4d39776cbeebf7e28ace92c47427dc6075d60f06..8858f25a900a19cc256d0b9d2d4c4bcca5de1b41 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/input/AutoCompleteHelper.kt
@@ -25,23 +25,18 @@ import android.support.v4.app.FragmentActivity
 import android.text.SpannableString
 import android.text.style.ForegroundColorSpan
 import android.text.style.StyleSpan
-import de.kuschku.libquassel.protocol.Buffer_Type
-import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.util.IrcUserUtils
-import de.kuschku.libquassel.util.flag.hasFlag
-import de.kuschku.libquassel.util.helpers.nullIf
 import de.kuschku.libquassel.util.helpers.value
 import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.AutoCompleteSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
-import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.styledAttributes
 import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.ui.TextDrawable
 import de.kuschku.quasseldroid.viewmodel.EditorViewModel
+import de.kuschku.quasseldroid.viewmodel.EditorViewModel.Companion.IGNORED_CHARS
 import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem
-import de.kuschku.quasseldroid.viewmodel.data.BufferStatus
 
 class AutoCompleteHelper(
   activity: FragmentActivity,
@@ -68,16 +63,20 @@ class AutoCompleteHelper(
     getColor(0, 0)
   }
 
-  private val avatarSize = activity.resources.getDimensionPixelSize(R.dimen.avatar_size)
-
   init {
     viewModel.autoCompleteData.toLiveData().observe(activity, Observer {
       val query = it?.first ?: ""
-      val shouldShowResults = (autoCompleteSettings.auto && query.length >= 3) ||
-                              (autoCompleteSettings.prefix && query.startsWith('@')) ||
-                              (autoCompleteSettings.prefix && query.startsWith('#'))
+      val shouldShowResults =
+        (autoCompleteSettings.auto && query.length >= 3) ||
+        (autoCompleteSettings.prefix && autoCompleteSettings.nicks && query.startsWith('@')) ||
+        (autoCompleteSettings.prefix && autoCompleteSettings.buffers && query.startsWith('#')) ||
+        (autoCompleteSettings.prefix && autoCompleteSettings.aliases && query.startsWith('/'))
       val list = if (shouldShowResults) it?.second.orEmpty() else emptyList()
-      val data = list.map {
+      val data = list.filter {
+        it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases ||
+        it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks ||
+        it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers
+      }.map {
         if (it is AutoCompleteItem.UserItem) {
           val nickName = it.nick
           val senderColorIndex = IrcUserUtils.senderColor(nickName)
@@ -144,74 +143,16 @@ class AutoCompleteHelper(
     this.dataListeners -= listener
   }
 
-  private fun autoCompleteDataFull(): List<AutoCompleteItem> {
-    return viewModel.rawAutoCompleteData.value?.let { (sessionOptional, id, lastWord) ->
-      val session = sessionOptional.orNull()
-      val bufferInfo = session?.bufferSyncer?.bufferInfo(id)
-      session?.networks?.let { networks ->
-        session.bufferSyncer?.bufferInfos()?.let { infos ->
-          if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
-            val network = networks[bufferInfo.networkId]
-            network?.ircChannel(
-              bufferInfo.bufferName
-            )?.let { ircChannel ->
-              val users = ircChannel.ircUsers()
-              val buffers = infos
-                .asSequence()
-                .filter {
-                  it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
-                }.mapNotNull { info ->
-                  networks[info.networkId]?.let { info to it }
-                }.map { (info, network) ->
-                  val channel = network.ircChannel(info.bufferName).nullIf { it == IrcChannel.NULL }
-                  AutoCompleteItem.ChannelItem(
-                    info = info,
-                    network = network.networkInfo(),
-                    bufferStatus = when (channel) {
-                      null -> BufferStatus.OFFLINE
-                      else -> BufferStatus.ONLINE
-                    },
-                    description = channel?.topic() ?: ""
-                  )
-                }
-              val nicks = users.asSequence().map { user ->
-                val userModes = ircChannel.userModes(user)
-                val prefixModes = network.prefixModes()
-
-                val lowestMode = userModes.mapNotNull(prefixModes::indexOf).min()
-                                 ?: prefixModes.size
-
-                AutoCompleteItem.UserItem(
-                  user.nick(),
-                  network.modesToPrefixes(userModes),
-                  lowestMode,
-                  user.realName(),
-                  user.isAway(),
-                  user.network().isMyNick(user.nick()),
-                  network.support("CASEMAPPING"),
-                  AvatarHelper.avatar(messageSettings, user, avatarSize)
-                )
-              }
-
-              (nicks + buffers).filter {
-                it.name.trimStart(*IGNORED_CHARS)
-                  .startsWith(
-                    lastWord.first.trimStart(*IGNORED_CHARS),
-                    ignoreCase = true
-                  )
-              }.sorted().toList()
-            }
-          } else null
-        }
-      }
-    } ?: emptyList()
-  }
-
   fun autoComplete(reverse: Boolean = false) {
     viewModel.lastWord.switchMap { it }.value?.let { originalWord ->
       val previous = autoCompletionState
       if (!originalWord.second.isEmpty()) {
-        val autoCompletedWords = autoCompleteDataFull()
+        val autoCompletedWords = viewModel.autoCompleteData.value?.second?.filter {
+          it is AutoCompleteItem.AliasItem && autoCompleteSettings.aliases ||
+          it is AutoCompleteItem.UserItem && autoCompleteSettings.nicks ||
+          it is AutoCompleteItem.ChannelItem && autoCompleteSettings.buffers
+        }.orEmpty()
+
         if (previous != null && originalWord.first == previous.originalWord && originalWord.second.start == previous.range.start) {
           val previousIndex = autoCompletedWords.indexOf(previous.completion)
           val autoCompletedWord = if (previousIndex != -1) {
@@ -252,8 +193,4 @@ class AutoCompleteHelper(
       }
     }
   }
-
-  companion object {
-    val IGNORED_CHARS = charArrayOf('-', '_', '[', ']', '{', '}', '|', '`', '^', '.', '\\', '@')
-  }
 }
diff --git a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
index 56d702b5e4b052d1efe513ec868b460e20a20ed9..6f97dc42c46cb72119853c6e3f78c7a43da1a572 100644
--- a/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
+++ b/app/src/main/java/de/kuschku/quasseldroid/ui/chat/nicks/NickListFragment.kt
@@ -47,7 +47,6 @@ import de.kuschku.quasseldroid.R
 import de.kuschku.quasseldroid.settings.AppearanceSettings
 import de.kuschku.quasseldroid.settings.MessageSettings
 import de.kuschku.quasseldroid.ui.chat.info.user.UserInfoActivity
-import de.kuschku.quasseldroid.ui.chat.input.AutoCompleteHelper.Companion.IGNORED_CHARS
 import de.kuschku.quasseldroid.util.avatars.AvatarHelper
 import de.kuschku.quasseldroid.util.helper.loadWithFallbacks
 import de.kuschku.quasseldroid.util.helper.styledAttributes
@@ -55,6 +54,7 @@ import de.kuschku.quasseldroid.util.helper.toLiveData
 import de.kuschku.quasseldroid.util.irc.format.IrcFormatDeserializer
 import de.kuschku.quasseldroid.util.service.ServiceBoundFragment
 import de.kuschku.quasseldroid.util.ui.TextDrawable
+import de.kuschku.quasseldroid.viewmodel.EditorViewModel.Companion.IGNORED_CHARS
 import de.kuschku.quasseldroid.viewmodel.data.Avatar
 import javax.inject.Inject
 
diff --git a/app/src/main/res/layout/widget_alias.xml b/app/src/main/res/layout/widget_alias.xml
new file mode 100644
index 0000000000000000000000000000000000000000..a2a17d1a6cd4ebfb633454e110125d7c6204d2a8
--- /dev/null
+++ b/app/src/main/res/layout/widget_alias.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  Quasseldroid - Quassel client for Android
+
+  Copyright (c) 2018 Janne Koschinski
+  Copyright (c) 2018 The Quassel Project
+
+  This program is free software: you can redistribute it and/or modify it
+  under the terms of the GNU General Public License version 3 as published
+  by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along
+  with this program.  If not, see <http://www.gnu.org/licenses/>.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  xmlns:tools="http://schemas.android.com/tools"
+  android:layout_width="match_parent"
+  android:layout_height="wrap_content"
+  android:background="?attr/backgroundMenuItem"
+  android:minHeight="48dp"
+  android:paddingBottom="8dp"
+  android:paddingLeft="16dp"
+  android:paddingRight="16dp"
+  android:paddingTop="8dp">
+
+  <LinearLayout
+    android:layout_width="0dip"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:layout_weight="1"
+    android:orientation="vertical">
+
+    <TextView
+      android:id="@+id/alias"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_vertical|start"
+      android:fontFamily="sans-serif-medium"
+      android:singleLine="true"
+      android:textColor="?attr/colorTextPrimary"
+      android:textSize="13sp"
+      tools:text="#quasseldroid" />
+
+    <TextView
+      android:id="@+id/expansion"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:layout_gravity="center_vertical|start"
+      android:singleLine="true"
+      android:textColor="?attr/colorTextSecondary"
+      android:textSize="12sp"
+      tools:text="Quasseldroid is an Android client for #quassel ♥ justJanne's much improved version: https://dl.kuschku.de/releases/quasseldroid/ ♥ http://github.com/sandsmark/Quasseldroid ♥ Quasseldroid  on play  https://market.android.com/details?id=com.iskrembilen.quasseldroid ♥ Sign up for beta: https://plus.google.com/communities/104094956084217666662" />
+  </LinearLayout>
+</LinearLayout>
diff --git a/app/src/main/res/values-de/strings_preferences.xml b/app/src/main/res/values-de/strings_preferences.xml
index 3f0abe89ace9de3921928b7482f44a2dc0f26252..4e7d0f140b2ad598879c5c5f3ddf5e7dcf305a6b 100644
--- a/app/src/main/res/values-de/strings_preferences.xml
+++ b/app/src/main/res/values-de/strings_preferences.xml
@@ -128,6 +128,12 @@
   <string name="preference_autocomplete_prefix_title">Nach Präfix anzeigen</string>
   <string name="preference_autocomplete_prefix_summary">Vervollständigt Namen und Chats automatisch nach einem @ oder #</string>
 
+  <string name="preference_autocomplete_nicks_title">Vervollständige Spitznamen</string>
+
+  <string name="preference_autocomplete_buffers_title">Vervollständige Chats</string>
+
+  <string name="preference_autocomplete_aliases_title">Vervollständige Aliase</string>
+
 
   <string name="preference_backlog_title">Verlauf</string>
 
diff --git a/app/src/main/res/values/strings_preferences.xml b/app/src/main/res/values/strings_preferences.xml
index 23cf0cbf92d1cd592171a9da8e27f0d7152bafcf..c2de68aca73f912dc3f770776e879f82b5726a5c 100644
--- a/app/src/main/res/values/strings_preferences.xml
+++ b/app/src/main/res/values/strings_preferences.xml
@@ -247,6 +247,15 @@
   <string name="preference_autocomplete_prefix_title">Show after prefix</string>
   <string name="preference_autocomplete_prefix_summary">Suggest nicks and channels after entering @ or #</string>
 
+  <string name="preference_autocomplete_nicks_key" translatable="false">autocomplete_nicks</string>
+  <string name="preference_autocomplete_nicks_title">Autocomplete Nicknames</string>
+
+  <string name="preference_autocomplete_buffers_key" translatable="false">autocomplete_buffers</string>
+  <string name="preference_autocomplete_buffers_title">Autocomplete Chats</string>
+
+  <string name="preference_autocomplete_aliases_key" translatable="false">autocomplete_aliases</string>
+  <string name="preference_autocomplete_aliases_title">Autocomplete Commands</string>
+
 
   <string name="preference_backlog_title">Backlog</string>
 
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 5f5fb5685b1cf6129b4d92eca22400fb786859cd..f350beadd5423521d1555d68f5c63006008d067d 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -245,6 +245,21 @@
       android:key="@string/preference_autocomplete_prefix_key"
       android:summary="@string/preference_autocomplete_prefix_summary"
       android:title="@string/preference_autocomplete_prefix_title" />
+
+    <SwitchPreference
+      android:defaultValue="true"
+      android:key="@string/preference_autocomplete_nicks_key"
+      android:title="@string/preference_autocomplete_nicks_title" />
+
+    <SwitchPreference
+      android:defaultValue="true"
+      android:key="@string/preference_autocomplete_buffers_key"
+      android:title="@string/preference_autocomplete_buffers_title" />
+
+    <SwitchPreference
+      android:defaultValue="true"
+      android:key="@string/preference_autocomplete_aliases_key"
+      android:title="@string/preference_autocomplete_aliases_title" />
   </PreferenceCategory>
 
   <PreferenceCategory android:layout="@layout/widget_preference_divider" />
diff --git a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
index f0c8e77fb08e3e4407216aba267b4936d7e0b800..3e26c5bcaebd8c4db46fb771fae29b99e2a8af63 100644
--- a/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
+++ b/lib/src/main/java/de/kuschku/libquassel/quassel/syncables/AliasManager.kt
@@ -28,6 +28,8 @@ import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager
 import de.kuschku.libquassel.quassel.syncables.interfaces.IAliasManager.Alias
 import de.kuschku.libquassel.quassel.syncables.interfaces.ISyncableObject
 import de.kuschku.libquassel.session.SignalProxy
+import io.reactivex.Observable
+import io.reactivex.subjects.BehaviorSubject
 import java.util.*
 
 class AliasManager constructor(
@@ -94,6 +96,8 @@ class AliasManager constructor(
     _aliases = list
   }
 
+  fun updates(): Observable<AliasManager> = live_updates.map { this }
+
   fun copy() = AliasManager(proxy).also {
     it.fromVariantMap(toVariantMap())
   }
@@ -188,7 +192,12 @@ class AliasManager constructor(
     }
   }
 
+  private val live_updates = BehaviorSubject.createDefault(Unit)
   private var _aliases = listOf<IAliasManager.Alias>()
+    set(value) {
+      field = value
+      live_updates.onNext(Unit)
+    }
 
   fun isEqual(other: AliasManager): Boolean =
     this.aliasList() == other.aliasList()
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
index b48d462b704d21f7b2f6b49000d0ce93f372d66e..00f17a98578b1171383b8e4ab4fecd0e3baeff2c 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/EditorViewModel.kt
@@ -21,12 +21,15 @@ package de.kuschku.quasseldroid.viewmodel
 
 import android.arch.lifecycle.ViewModel
 import de.kuschku.libquassel.protocol.Buffer_Type
+import de.kuschku.libquassel.quassel.syncables.AliasManager
 import de.kuschku.libquassel.quassel.syncables.IrcChannel
 import de.kuschku.libquassel.quassel.syncables.IrcUser
+import de.kuschku.libquassel.quassel.syncables.Network
 import de.kuschku.libquassel.session.ISession
 import de.kuschku.libquassel.util.Optional
 import de.kuschku.libquassel.util.flag.hasFlag
 import de.kuschku.libquassel.util.helpers.mapNullable
+import de.kuschku.libquassel.util.helpers.nullIf
 import de.kuschku.quasseldroid.util.helper.combineLatest
 import de.kuschku.quasseldroid.viewmodel.data.AutoCompleteItem
 import de.kuschku.quasseldroid.viewmodel.data.BufferStatus
@@ -51,7 +54,7 @@ class EditorViewModel : ViewModel() {
         }
     }
 
-  val autoCompleteData = rawAutoCompleteData
+  val autoCompleteData: Observable<Pair<String, List<AutoCompleteItem>>> = rawAutoCompleteData
     .distinctUntilChanged()
     .debounce(300, TimeUnit.MILLISECONDS)
     .switchMap { (sessionOptional, id, lastWord) ->
@@ -61,78 +64,97 @@ class EditorViewModel : ViewModel() {
       if (bufferSyncer != null) {
         session.liveNetworks().switchMap { networks ->
           bufferSyncer.liveBufferInfos().switchMap { infos ->
-            if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
-              val network = networks[bufferInfo.networkId]
-              val ircChannel = network?.ircChannel(
-                bufferInfo.bufferName
-              )
-              if (ircChannel != null) {
-                ircChannel.liveIrcUsers().switchMap { users ->
-                  val buffers: List<Observable<AutoCompleteItem.ChannelItem>?> = infos.values
-                    .filter {
-                      it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
-                    }.mapNotNull { info ->
-                      networks[info.networkId]?.let { info to it }
-                    }.map { (info, network) ->
-                      network.liveIrcChannel(
-                        info.bufferName
-                      ).switchMap { channel ->
-                        channel.updates().mapNullable(IrcChannel.NULL) {
-                          AutoCompleteItem.ChannelItem(
-                            info = info,
-                            network = network.networkInfo(),
-                            bufferStatus = when (it) {
-                              null -> BufferStatus.OFFLINE
-                              else -> BufferStatus.ONLINE
-                            },
-                            description = it?.topic() ?: ""
+            (session.aliasManager?.updates()?.map(AliasManager::aliasList)
+             ?: Observable.just(emptyList())).switchMap { aliases ->
+              val network = networks[bufferInfo?.networkId] ?: Network.NULL
+              val ircChannel = if (bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true) {
+                network.ircChannel(bufferInfo.bufferName) ?: IrcChannel.NULL
+              } else IrcChannel.NULL
+              ircChannel.liveIrcUsers().switchMap { users ->
+                fun processResults(results: List<Observable<out AutoCompleteItem>>) =
+                  combineLatest<AutoCompleteItem>(results)
+                    .map { list ->
+                      val filtered = list.filter {
+                        it.name.trimStart(*IGNORED_CHARS)
+                          .startsWith(
+                            lastWord.first.trimStart(*IGNORED_CHARS),
+                            ignoreCase = true
                           )
-                        }
                       }
+                      Pair(
+                        lastWord.first,
+                        filtered.sorted()
+                      )
                     }
-                  val nicks = users.map<IrcUser, Observable<AutoCompleteItem.UserItem>?> {
-                    it.updates().map { user ->
-                      val userModes = ircChannel.userModes(user)
-                      val prefixModes = network.prefixModes()
 
-                      val lowestMode = userModes.mapNotNull(prefixModes::indexOf).min()
-                                       ?: prefixModes.size
+                fun getAliases() = aliases.map {
+                  Observable.just(AutoCompleteItem.AliasItem(
+                    it.name,
+                    it.expansion
+                  ))
+                }
 
-                      AutoCompleteItem.UserItem(
-                        user.nick(),
-                        network.modesToPrefixes(userModes),
-                        lowestMode,
-                        user.realName(),
-                        user.isAway(),
-                        user.network().isMyNick(user.nick()),
-                        network.support("CASEMAPPING")
-                      )
+                fun getBuffers() = infos.values
+                  .filter {
+                    it.type.toInt() == Buffer_Type.ChannelBuffer.toInt()
+                  }.mapNotNull { info ->
+                    networks[info.networkId]?.let { info to it }
+                  }.map { (info, network) ->
+                    network.liveIrcChannel(
+                      info.bufferName
+                    ).switchMap { channel ->
+                      channel.updates().mapNullable(IrcChannel.NULL) {
+                        AutoCompleteItem.ChannelItem(
+                          info = info,
+                          network = network.networkInfo(),
+                          bufferStatus = when (it) {
+                            null -> BufferStatus.OFFLINE
+                            else -> BufferStatus.ONLINE
+                          },
+                          description = it?.topic() ?: ""
+                        )
+                      }
                     }
                   }
 
-                  combineLatest<AutoCompleteItem>(nicks + buffers)
-                    .map { list ->
-                      val ignoredStartingCharacters = charArrayOf(
-                        '-', '_', '[', ']', '{', '}', '|', '`', '^', '.', '\\', '@'
-                      )
+                fun getUsers(): Set<IrcUser> = when {
+                  bufferInfo?.type?.hasFlag(Buffer_Type.ChannelBuffer) == true ->
+                    users
+                  bufferInfo?.type?.hasFlag(Buffer_Type.QueryBuffer) == true   ->
+                    network.ircUser(bufferInfo.bufferName).nullIf { it == IrcUser.NULL }?.let {
+                      setOf(it)
+                    } ?: emptySet()
+                  else                                                         ->
+                    emptySet()
+                }
 
-                      Pair(
-                        lastWord.first,
-                        list.filter {
-                          it.name.trimStart(*ignoredStartingCharacters)
-                            .startsWith(
-                              lastWord.first.trimStart(*ignoredStartingCharacters),
-                              ignoreCase = true
-                            )
-                        }.sorted()
-                      )
-                    }
+                fun getNicks() = getUsers().map<IrcUser, Observable<AutoCompleteItem.UserItem>> {
+                  it.updates().map { user ->
+                    val userModes = ircChannel.userModes(user)
+                    val prefixModes = network.prefixModes()
+
+                    val lowestMode = userModes.mapNotNull(prefixModes::indexOf).min()
+                                     ?: prefixModes.size
+
+                    AutoCompleteItem.UserItem(
+                      user.nick(),
+                      network.modesToPrefixes(userModes),
+                      lowestMode,
+                      user.realName(),
+                      user.isAway(),
+                      user.network().isMyNick(user.nick()),
+                      network.support("CASEMAPPING")
+                    )
+                  }
+                }
+
+                when (lastWord.first.firstOrNull()) {
+                  '/'  -> processResults(getAliases())
+                  '@'  -> processResults(getNicks())
+                  '#'  -> processResults(getBuffers())
+                  else -> processResults(getAliases() + getNicks() + getBuffers())
                 }
-              } else {
-                Observable.just(Pair(lastWord.first, emptyList()))
               }
-            } else {
-              Observable.just(Pair(lastWord.first, emptyList()))
             }
           }
         }
@@ -140,4 +162,10 @@ class EditorViewModel : ViewModel() {
         Observable.just(Pair(lastWord.first, emptyList()))
       }
     }
+
+  companion object {
+    val IGNORED_CHARS = charArrayOf(
+      '-', '_', '[', ']', '{', '}', '|', '`', '^', '.', '\\', '@', '#', '/'
+    )
+  }
 }
diff --git a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
index 88f2f5424b01ace72eab92debf11ad1b2cb3b242..2e7d7fc7b741ac082532de286edac0c9b01686f2 100644
--- a/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
+++ b/viewmodel/src/main/java/de/kuschku/quasseldroid/viewmodel/data/AutoCompleteItem.kt
@@ -23,15 +23,11 @@ import android.graphics.drawable.Drawable
 import de.kuschku.libquassel.quassel.BufferInfo
 import de.kuschku.libquassel.quassel.syncables.interfaces.INetwork
 
-sealed class AutoCompleteItem(open val name: String) : Comparable<AutoCompleteItem> {
-  override fun compareTo(other: AutoCompleteItem): Int {
-    return when {
-      this is UserItem &&
-      other is ChannelItem -> -1
-      this is ChannelItem &&
-      other is UserItem    -> 1
-      else                 -> this.name.compareTo(other.name)
-    }
+sealed class AutoCompleteItem(open val name: String, private val type: Int) :
+  Comparable<AutoCompleteItem> {
+  override fun compareTo(other: AutoCompleteItem) = when {
+    this.type != other.type -> this.type.compareTo(other.type)
+    else                    -> this.name.compareTo(other.name)
   }
 
   data class UserItem(
@@ -45,12 +41,17 @@ sealed class AutoCompleteItem(open val name: String) : Comparable<AutoCompleteIt
     val avatarUrls: List<Avatar> = emptyList(),
     val fallbackDrawable: Drawable? = null,
     val displayNick: CharSequence? = null
-  ) : AutoCompleteItem(nick)
+  ) : AutoCompleteItem(nick, 0)
+
+  data class AliasItem(
+    val alias: String,
+    val expansion: String
+  ) : AutoCompleteItem(alias, 1)
 
   data class ChannelItem(
     val info: BufferInfo,
     val network: INetwork.NetworkInfo,
     val bufferStatus: BufferStatus,
     val description: CharSequence
-  ) : AutoCompleteItem(info.bufferName ?: "")
+  ) : AutoCompleteItem(info.bufferName ?: "", 2)
 }