diff --git a/changelog.md b/changelog.md
index 54008f5899c22b35e72a7f8ff23bd9085e8ea71b..74c3cb5f9d5f87e99b89420d77e4cae375dfb8fa 100644
--- a/changelog.md
+++ b/changelog.md
@@ -5,6 +5,7 @@
 * `CHG` hover: improve showing multi defines
 * `CHG` hint: `Lua.hint.paramName` now supports `Disable`, `Literal` and `All`
 * `CHG` completion: new setting `Lua.completion.showWord`
+* `CHG` completion: new setting `Lua.completion.requireSeparator`
 * `CHG` no longer ignore file names case in Windows
 * `CHG` watching library changes
 * `FIX` runtime errors
diff --git a/script/config/config.lua b/script/config/config.lua
index 91f0a00b88f026c2239cc9f99d78cd06b33dd233..297d70ef05009017297a78586ebf27f910a2b218 100644
--- a/script/config/config.lua
+++ b/script/config/config.lua
@@ -184,6 +184,7 @@ local Template = {
     ['Lua.completion.showWord']             = Type.String  >> 'Enable',
     ['Lua.completion.autoRequire']          = Type.Boolean >> true,
     ['Lua.completion.showParams']           = Type.Boolean >> true,
+    ['Lua.completion.requireSeparator']     = Type.String  >> '.',
     ['Lua.signatureHelp.enable']            = Type.Boolean >> true,
     ['Lua.hover.enable']                    = Type.Boolean >> true,
     ['Lua.hover.viewString']                = Type.Boolean >> true,
@@ -324,6 +325,7 @@ function m.update(new)
     expand(new)
 end
 
+---@param callback fun(key: string, value: any, oldValue: any)
 function m.watch(callback)
     m.watchList[#m.watchList+1] = callback
 end
diff --git a/script/provider/completion.lua b/script/provider/completion.lua
index 92546571df032900a038b8a33ff56dbc31c306e5..f1698249493cb7d28aa21632eb039f812d7e465d 100644
--- a/script/provider/completion.lua
+++ b/script/provider/completion.lua
@@ -6,7 +6,7 @@ local config = require 'config'
 local isEnable = false
 
 local function allWords()
-    local str = '\t\n.:(\'"[,#*@|=-{ '
+    local str = '\t\n.:(\'"[,#*@|=-{/\\ '
     local list = {}
     for c in str:gmatch '.' do
         list[#list+1] = c
diff --git a/script/workspace/require-path.lua b/script/workspace/require-path.lua
index 096b7f751e25ccba04359069b60b80411ad785f5..2ec2918c718ab6217dcda74498fd67932e727334 100644
--- a/script/workspace/require-path.lua
+++ b/script/workspace/require-path.lua
@@ -1,19 +1,21 @@
-local platform = require 'bee.platform'
-local files    = require 'files'
-local furi     = require 'file-uri'
+local platform  = require 'bee.platform'
+local files     = require 'files'
+local furi      = require 'file-uri'
 local workspace = require "workspace"
+local config    = require 'config'
 local m = {}
 
 m.cache = {}
 
 --- `aaa/bbb/ccc.lua` 与 `?.lua` 将返回 `aaa.bbb.cccc`
 local function getOnePath(path, searcher)
+    local separator    = config.get 'Lua.completion.requireSeparator'
     local stemPath     = path
                         : gsub('%.[^%.]+$', '')
-                        : gsub('[/\\]+', '.')
+                        : gsub('[/\\%.]+', separator)
     local stemSearcher = searcher
                         : gsub('%.[^%.]+$', '')
-                        : gsub('[/\\]+', '.')
+                        : gsub('[/\\%.]+', separator)
     local start        = stemSearcher:match '()%?' or 1
     for pos = start, #stemPath do
         local word = stemPath:sub(start, pos)
@@ -88,4 +90,10 @@ files.watch(function (ev)
     end
 end)
 
+config.watch(function (key, value, oldValue)
+    if key == 'Lua.completion.requireSeparator' then
+        m.flush()
+    end
+end)
+
 return m
diff --git a/test/crossfile/completion.lua b/test/crossfile/completion.lua
index 927cad3cb7719dc1248b6df8850e7707a76a2509..d26398caa3861536a9a9af7459493e60f668fc8c 100644
--- a/test/crossfile/completion.lua
+++ b/test/crossfile/completion.lua
@@ -302,6 +302,32 @@ TEST {
     }
 }
 
+local originSeparator = config.get 'Lua.completion.requireSeparator'
+config.set('Lua.completion.requireSeparator', '/')
+TEST {
+    {
+        path = 'abc.lua',
+        content = '',
+    },
+    {
+        path = 'abc/init.lua',
+        content = '',
+    },
+    {
+        path = 'test.lua',
+        content = 'require "abc/i$"',
+        main = true,
+    },
+    completion = {
+        {
+            label = 'abc/init',
+            kind = CompletionItemKind.Reference,
+            textEdit = EXISTS,
+        },
+    }
+}
+config.set('Lua.completion.requireSeparator', originSeparator)
+
 TEST {
     {
         path = 'core/core.lua',
diff --git a/test/crossfile/hover.lua b/test/crossfile/hover.lua
index f00f6e35f354a8b747a94821506d91797ea056fd..2ceeb597c79116d17cebdc174a58a4e2022bf151 100644
--- a/test/crossfile/hover.lua
+++ b/test/crossfile/hover.lua
@@ -108,6 +108,42 @@ TEST {
 ---
 * [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `Folder\?.lua`)]],
 }
+
+TEST {
+    {
+        path = 'Folder/a.lua',
+        content = '',
+    },
+    {
+        path = 'b.lua',
+        content = 'require <?"Folder.a"?>',
+    },
+    hover = [[
+```lua
+8 个字节
+```
+
+---
+* [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `?.lua`)]],
+}
+
+TEST {
+    {
+        path = 'Folder/a.lua',
+        content = '',
+    },
+    {
+        path = 'b.lua',
+        content = 'require <?"Folder/a"?>',
+    },
+    hover = [[
+```lua
+8 个字节
+```
+
+---
+* [Folder\a.lua](file:///Folder/a.lua) (搜索路径: `?.lua`)]],
+}
 else
 TEST {
     {