and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
+### Removed
+* Expansion of `%` to the current opened URI for `:shellcmd` was removed
+  because it breaks the `x-hint-command` with URIs containing '%'. But it is
+  still possible to use `$VIMB_URI` for the `:shellcmd` which has the same
+  effect.
 
 ## [3.6.0] - 2020-01-02
 ### Added
 
 static gboolean parse_command_name(Client *c, const char **input, ExArg *arg);
 static gboolean parse_bang(const char **input, ExArg *arg);
 static gboolean parse_lhs(const char **input, ExArg *arg);
-static gboolean parse_rhs(Client *c, const char **input, ExArg *arg);
+static gboolean parse_rhs(const char **input, ExArg *arg);
 static void skip_whitespace(const char **input);
 static void free_cmdarg(ExArg *arg);
 static VbCmdResult execute(Client *c, const ExArg *arg);
     }
     /* parse the rhs if this is available */
     skip_whitespace(input);
-    parse_rhs(c, input, arg);
+    parse_rhs(input, arg);
 
     if (**input) {
         (*input)++;
  * command can contain any char accept of the newline, else the right hand
  * side end on the first none escaped | or newline.
  */
-static gboolean parse_rhs(Client *c, const char **input, ExArg *arg)
+static gboolean parse_rhs(const char **input, ExArg *arg)
 {
     int expflags, flags;
     gboolean cmdlist;
 
     cmdlist  = (arg->flags & EX_FLAG_CMD) != 0;
     expflags = (arg->flags & EX_FLAG_EXP)
-        ? UTIL_EXP_TILDE|UTIL_EXP_DOLLAR|UTIL_EXP_SPECIAL
+        ? UTIL_EXP_TILDE|UTIL_EXP_DOLLAR
         : 0;
     flags = expflags;
 
      * EX_FLAG_CMD is not set also on | */
     while (**input && **input != '\n' && (cmdlist || **input != '|')) {
         /* check for expansion placeholder */
-        util_parse_expansion(c->state, input, arg->rhs, flags, "|\\");
+        util_parse_expansion(input, arg->rhs, flags, "|\\");
 
         if (VB_IS_SEPARATOR(**input)) {
             /* add tilde expansion for next loop needs to be first char or to
 
                 case EX_SAVE: /* Fallthrough */
                 case EX_SOURCE:
-                    found = util_filename_fill_completion(c->state, store, token);
+                    found = util_filename_fill_completion(store, token);
                     break;
 
 #ifdef FEATURE_AUTOCMD
 
 
     /* Prepare the path to save the download. */
     if (path && *path) {
-        file = util_build_path(c->state, path, download_path);
+        file = util_build_path(path, download_path);
 
         /* if file is an directory append a file name */
         if (g_file_test(file, (G_FILE_TEST_IS_DIR))) {
             g_free(dir);
         }
     } else {
-        file = util_build_path(c->state, suggested_filename, download_path);
+        file = util_build_path(suggested_filename, download_path);
     }
 
     g_free(basename);
 
  *
  * Returned path must be freed.
  */
-char *util_build_path(State state, const char *path, const char *dir)
+char *util_build_path(const char *path, const char *dir)
 {
     char *fullPath = NULL, *fexp, *dexp, *p;
     int expflags   = UTIL_EXP_TILDE|UTIL_EXP_DOLLAR;
 
     /* if the path could be expanded */
-    if ((fexp = util_expand(state, path, expflags))) {
+    if ((fexp = util_expand(path, expflags))) {
         if (*fexp == '/') {
             /* path is already absolute, no need to use given dir - there is
              * no need to free fexp, because this should be done by the caller
             fullPath = fexp;
         } else if (dir && *dir) {
             /* try to expand also the dir given - this may be ~/path */
-            if ((dexp = util_expand(state, dir, expflags))) {
+            if ((dexp = util_expand(dir, expflags))) {
                 /* use expanded dir and append expanded path */
                 fullPath = g_build_filename(dexp, fexp, NULL);
                 g_free(dexp);
  *
  * Returned path must be g_freed.
  */
-char *util_expand(State state, const char *src, int expflags)
+char *util_expand(const char *src, int expflags)
 {
     const char **input = &src;
     char *result;
     int flags    = expflags;
 
     while (**input) {
-        util_parse_expansion(state, input, dst, flags, "\\");
+        util_parse_expansion(input, dst, flags, "\\");
         if (VB_IS_SEPARATOR(**input)) {
             /* after space the tilde expansion is allowed */
             flags = expflags;
  * Fills file path completion entries into given list store for also given
  * input.
  */
-gboolean util_filename_fill_completion(State state, GtkListStore *store, const char *input)
+gboolean util_filename_fill_completion(GtkListStore *store, const char *input)
 {
     gboolean found = FALSE;
     GError *error  = NULL;
     input_basename = last_slash ? last_slash + 1 : input;
     input_dirname  = g_strndup(input, input_basename - input);
     real_dirname   = util_expand(
-        state,
         *input_dirname ? input_dirname : ".",
-        UTIL_EXP_TILDE|UTIL_EXP_DOLLAR|UTIL_EXP_SPECIAL
+        UTIL_EXP_TILDE|UTIL_EXP_DOLLAR
     );
 
     dir = g_dir_open(real_dirname, 0, &error);
  * @quoteable: String of chars that are additionally escapable by \.
  * Returns TRUE if input started with expandable pattern.
  */
-gboolean util_parse_expansion(State state, const char **input, GString *str,
+gboolean util_parse_expansion(const char **input, GString *str,
         int flags, const char *quoteable)
 {
     GString *name;
         /* variable are expanded even if they do not exists */
         expanded = TRUE;
         g_string_free(name, TRUE);
-    } else if (flags & UTIL_EXP_SPECIAL && **input == '%') {
-        if (state.uri) {
-            /* TODO check for modifiers like :h:t:r:e */
-            g_string_append(str, state.uri);
-            expanded = TRUE;
-        }
     }
 
     if (!expanded) {
             } else if (strchr(quoteable, **input)
                 || (flags & UTIL_EXP_TILDE && **input == '~')
                 || (flags & UTIL_EXP_DOLLAR && **input == '$')
-                || (flags & UTIL_EXP_SPECIAL && **input == '%')
             ) {
                 /* escaped char becomes only char */
                 g_string_append_c(str, **input);
 
 enum {
     UTIL_EXP_TILDE   = 0x01, /* ~/ and ~user expansion */
     UTIL_EXP_DOLLAR  = 0x02, /* $ENV and ${ENV} expansion */
-    UTIL_EXP_SPECIAL = 0x04, /* expand % to current URI */
 };
 typedef void *(*Util_Content_Func)(const char*, const char*);
 
-char *util_build_path(State state, const char *path, const char *dir);
+char *util_build_path(const char *path, const char *dir);
 void util_cleanup(void);
 gboolean util_create_dir_if_not_exists(const char *dirpath);
 gboolean util_create_tmp_file(const char *content, char **file);
-char *util_expand(State state, const char *src, int expflags);
+char *util_expand(const char *src, int expflags);
 gboolean util_file_append(const char *file, const char *format, ...);
 gboolean util_file_prepend(const char *file, const char *format, ...);
 void util_file_prepend_line(const char *file, const char *line,
 GList *util_strv_to_unique_list(char **lines, Util_Content_Func func,
         guint max_items);
 gboolean util_fill_completion(GtkListStore *store, const char *input, GList *src);
-gboolean util_filename_fill_completion(State state, GtkListStore *store, const char *input);
+gboolean util_filename_fill_completion(GtkListStore *store, const char *input);
 char *util_js_result_as_string(WebKitJavascriptResult *result);
 double util_js_result_as_number(WebKitJavascriptResult *result);
-gboolean util_parse_expansion(State state, const char **input, GString *str,
-        int flags, const char *quoteable);
+gboolean util_parse_expansion(const char **input, GString *str, int flags,
+        const char *quoteable);
 char *util_sanitize_filename(char *filename);
 char *util_sanitize_uri(const char *uri_str);
 char *util_strcasestr(const char *haystack, const char *needle);
 
 #include <gtk/gtk.h>
 #include <src/util.h>
 
-static void check_expand(State state, const char *str, const char *expected)
+static void check_expand(const char *str, const char *expected)
 {
-    char *result = util_expand(state, str, UTIL_EXP_DOLLAR|UTIL_EXP_TILDE|UTIL_EXP_SPECIAL);
+    char *result = util_expand(str, UTIL_EXP_DOLLAR|UTIL_EXP_TILDE);
     g_assert_cmpstr(result, ==, expected);
     g_free(result);
 }
 
 static void test_expand_evn(void)
 {
-    State state = {0};
     /* set environment var for testing expansion */
     g_setenv("VIMB_VAR", "value", true);
 
-    check_expand(state, "$VIMB_VAR", "value");
-    check_expand(state, "$VIMB_VAR", "value");
-    check_expand(state, "$VIMB_VAR$VIMB_VAR", "valuevalue");
-    check_expand(state, "${VIMB_VAR}", "value");
-    check_expand(state, "my$VIMB_VAR", "myvalue");
-    check_expand(state, "'$VIMB_VAR'", "'value'");
-    check_expand(state, "${VIMB_VAR}s ", "values ");
+    check_expand("$VIMB_VAR", "value");
+    check_expand("$VIMB_VAR", "value");
+    check_expand("$VIMB_VAR$VIMB_VAR", "valuevalue");
+    check_expand("${VIMB_VAR}", "value");
+    check_expand("my$VIMB_VAR", "myvalue");
+    check_expand("'$VIMB_VAR'", "'value'");
+    check_expand("${VIMB_VAR}s ", "values ");
 
     g_unsetenv("UNKNOWN");
 
-    check_expand(state, "$UNKNOWN", "");
-    check_expand(state, "${UNKNOWN}", "");
-    check_expand(state, "'$UNKNOWN'", "''");
+    check_expand("$UNKNOWN", "");
+    check_expand("${UNKNOWN}", "");
+    check_expand("'$UNKNOWN'", "''");
 }
 
 static void test_expand_escaped(void)
 {
-    State state = {0};
     g_setenv("VIMB_VAR", "value", true);
 
-    check_expand(state, "\\$VIMB_VAR", "$VIMB_VAR");
-    check_expand(state, "\\${VIMB_VAR}", "${VIMB_VAR}");
-    check_expand(state, "\\~/", "~/");
-    check_expand(state, "\\~/vimb", "~/vimb");
-    check_expand(state, "\\~root", "~root");
-    check_expand(state, "\\%", "%");
-    check_expand(state, "\\\\$VIMB_VAR", "\\value");         /* \\$VAR becomes \ExpandedVar */
-    check_expand(state, "\\\\\\$VIMB_VAR", "\\$VIMB_VAR");   /* \\\$VAR becomes \$VAR */
+    check_expand("\\$VIMB_VAR", "$VIMB_VAR");
+    check_expand("\\${VIMB_VAR}", "${VIMB_VAR}");
+    check_expand("\\~/", "~/");
+    check_expand("\\~/vimb", "~/vimb");
+    check_expand("\\~root", "~root");
+    check_expand("\\\\$VIMB_VAR", "\\value");         /* \\$VAR becomes \ExpandedVar */
+    check_expand("\\\\\\$VIMB_VAR", "\\$VIMB_VAR");   /* \\\$VAR becomes \$VAR */
 }
 
 static void test_expand_tilde_home(void)
 {
-    State state = {0};
     char *dir;
     const char *home = g_get_home_dir();
 
-    check_expand(state, "~", "~");
-    check_expand(state, "~/", home);
-    check_expand(state, "foo~/bar", "foo~/bar");
-    check_expand(state, "~/foo", (dir = g_strdup_printf("%s/foo", home)));
+    check_expand("~", "~");
+    check_expand("~/", home);
+    check_expand("foo~/bar", "foo~/bar");
+    check_expand("~/foo", (dir = g_strdup_printf("%s/foo", home)));
     g_free(dir);
 
-    check_expand(state, "foo ~/bar", (dir = g_strdup_printf("foo %s/bar", home)));
+    check_expand("foo ~/bar", (dir = g_strdup_printf("foo %s/bar", home)));
     g_free(dir);
 
-    check_expand(state, "~/~", (dir = g_strdup_printf("%s/~", home)));
+    check_expand("~/~", (dir = g_strdup_printf("%s/~", home)));
     g_free(dir);
 
-    check_expand(state, "~/~/", (dir = g_strdup_printf("%s/~/", home)));
+    check_expand("~/~/", (dir = g_strdup_printf("%s/~/", home)));
     g_free(dir);
 }
 
 static void test_expand_tilde_user(void)
 {
-    State state = {0};
     const char *user = g_get_user_name();
     const char *home;
     char *in, *out;
 
     /* don't expand within words */
     in = g_strdup_printf("foo~%s/bar", user);
-    check_expand(state, in, in);
+    check_expand(in, in);
     g_free(in);
 
-    check_expand(state, (in = g_strdup_printf("foo ~%s", user)), (out = g_strdup_printf("foo %s", home)));
+    check_expand((in = g_strdup_printf("foo ~%s", user)), (out = g_strdup_printf("foo %s", home)));
     g_free(in);
     g_free(out);
 
-    check_expand(state, (in = g_strdup_printf("~%s", user)), home);
+    check_expand((in = g_strdup_printf("~%s", user)), home);
     g_free(in);
 
-    check_expand(state, (in = g_strdup_printf("~%s/bar", user)), (out = g_strdup_printf("%s/bar", home)));
+    check_expand((in = g_strdup_printf("~%s/bar", user)), (out = g_strdup_printf("%s/bar", home)));
     g_free(in);
     g_free(out);
 }
 
-static void test_expand_speacial(void)
-{
-    State state = {.uri = "http://fanglingsu.github.io/vimb/"};
-
-    check_expand(state, "%", "http://fanglingsu.github.io/vimb/");
-    check_expand(state, "'%'", "'http://fanglingsu.github.io/vimb/'");
-}
-
 static void test_strcasestr(void)
 {
     g_assert_nonnull(util_strcasestr("Vim like Browser", "browser"));
     g_test_add_func("/test-util/expand-escaped", test_expand_escaped);
     g_test_add_func("/test-util/expand-tilde-home", test_expand_tilde_home);
     g_test_add_func("/test-util/expand-tilde-user", test_expand_tilde_user);
-    g_test_add_func("/test-util/expand-spacial", test_expand_speacial);
     g_test_add_func("/test-util/strcasestr", test_strcasestr);
     g_test_add_func("/test-util/str_replace", test_str_replace);
     g_test_add_func("/test-util/wildmatch-simple", test_wildmatch_simple);