def s:processDefDeclReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
   if reply.result->empty()
     util.WarnMsg("Error: definition is not found")
-    # pop the tag stack
-    var tagstack: dict<any> = gettagstack()
-    if tagstack.length > 0
-      settagstack(winnr(), {curidx: tagstack.length}, 't')
+    if !lspserver.peekDefDecl
+      # pop the tag stack
+      var tagstack: dict<any> = gettagstack()
+      if tagstack.length > 0
+        settagstack(winnr(), {curidx: tagstack.length}, 't')
+      endif
     endif
+    lspserver.peekDefDecl = false
     return
   endif
 
     location = reply.result
   endif
   var fname = util.LspUriToFile(location.uri)
-  var wid = fname->bufwinid()
-  if wid != -1
-    wid->win_gotoid()
+  if lspserver.peekDefDecl
+    # open the definition/declaration in the preview window and highlight the
+    # matching symbol
+    exe 'pedit ' .. fname
+    var cur_wid = win_getid()
+    wincmd P
+    var pvwbuf = bufnr()
+    setcursorcharpos(location.range.start.line + 1,
+                       location.range.start.character + 1)
+    silent! matchdelete(101)
+    var pos: list<number> = []
+    var start_col: number
+    var end_col: number
+    start_col = util.GetLineByteFromPos(pvwbuf, location.range.start) + 1
+    end_col = util.GetLineByteFromPos(pvwbuf, location.range.end) + 1
+    pos->add(location.range.start.line + 1)
+    pos->extend([start_col, end_col - start_col])
+    matchaddpos('Search', [pos], 10, 101)
+    win_gotoid(cur_wid)
   else
-    var bnr: number = fname->bufnr()
-    if bnr != -1
-      if &modified || &buftype != ''
-       exe 'sbuffer ' .. bnr
-      else
-       exe 'buf ' .. bnr
-      endif
+    # jump to the file and line containing the symbol
+    var wid = fname->bufwinid()
+    if wid != -1
+      wid->win_gotoid()
     else
-      if &modified || &buftype != ''
-       # if the current buffer has unsaved changes, then open the file in a
-       # new window
-       exe 'split ' .. fname
+      var bnr: number = fname->bufnr()
+      if bnr != -1
+        if &modified || &buftype != ''
+          exe 'sbuffer ' .. bnr
+        else
+          exe 'buf ' .. bnr
+        endif
       else
-       exe 'edit  ' .. fname
+        if &modified || &buftype != ''
+          # if the current buffer has unsaved changes, then open the file in a
+          # new window
+          exe 'split ' .. fname
+        else
+          exe 'edit  ' .. fname
+        endif
       endif
     endif
-  endif
-  # Set the previous cursor location mark
-  setpos("'`", getcurpos())
-  setcursorcharpos(location.range.start.line + 1,
+    # Set the previous cursor location mark
+    setpos("'`", getcurpos())
+    setcursorcharpos(location.range.start.line + 1,
                        location.range.start.character + 1)
+  endif
   redraw!
+  lspserver.peekDefDecl = false
 enddef
 
 # process the 'textDocument/signatureHelp' reply from the LSP server
 
 enddef
 
 # Go to a definition using "textDocument/definition" LSP request
-def lsp#gotoDefinition()
+def lsp#gotoDefinition(peek: bool)
   var ftype: string = &filetype
   if ftype == '' || @% == ''
     return
     return
   endif
 
-  lspserver.gotoDefinition()
+  lspserver.gotoDefinition(peek)
 enddef
 
 # Go to a declaration using "textDocument/declaration" LSP request
-def lsp#gotoDeclaration()
+def lsp#gotoDeclaration(peek: bool)
   var ftype: string = &filetype
   if ftype == ''
     return
     return
   endif
 
-  lspserver.gotoDeclaration()
+  lspserver.gotoDeclaration(peek)
 enddef
 
 # Go to a type definition using "textDocument/typeDefinition" LSP request
-def lsp#gotoTypedef()
+def lsp#gotoTypedef(peek: bool)
   var ftype: string = &filetype
   if ftype == ''
     return
     return
   endif
 
-  lspserver.gotoTypeDef()
+  lspserver.gotoTypeDef(peek)
 enddef
 
 # Go to a implementation using "textDocument/implementation" LSP request
-def lsp#gotoImplementation()
+def lsp#gotoImplementation(peek: bool)
   var ftype: string = &filetype
   if ftype == ''
     return
     return
   endif
 
-  lspserver.gotoImplementation()
+  lspserver.gotoImplementation(peek)
 enddef
 
 # Show the signature using "textDocument/signatureHelp" LSP method
 
 
 # Request: "textDocument/definition"
 # Param: DefinitionParams
-def s:gotoDefinition(lspserver: dict<any>): void
+def s:gotoDefinition(lspserver: dict<any>, peek: bool): void
   # Check whether LSP server supports jumping to a definition
   if !lspserver.caps->has_key('definitionProvider')
                                || !lspserver.caps.definitionProvider
     return
   endif
 
-  util.PushCursorToTagStack()
+  if !peek
+    util.PushCursorToTagStack()
+  endif
+  lspserver.peekDefDecl = peek
   var req = lspserver.createRequest('textDocument/definition')
   # interface DefinitionParams
   #   interface TextDocumentPositionParams
 
 # Request: "textDocument/declaration"
 # Param: DeclarationParams
-def s:gotoDeclaration(lspserver: dict<any>): void
+def s:gotoDeclaration(lspserver: dict<any>, peek: bool): void
   # Check whether LSP server supports jumping to a declaration
   if !lspserver.caps->has_key('declarationProvider')
                        || !lspserver.caps.declarationProvider
     return
   endif
 
-  util.PushCursorToTagStack()
+  if !peek
+    util.PushCursorToTagStack()
+  endif
+  lspserver.peekDefDecl = peek
   var req = lspserver.createRequest('textDocument/declaration')
 
   # interface DeclarationParams
 
 # Request: "textDocument/typeDefinition"
 # Param: TypeDefinitionParams
-def s:gotoTypeDef(lspserver: dict<any>): void
+def s:gotoTypeDef(lspserver: dict<any>, peek: bool): void
   # Check whether LSP server supports jumping to a type definition
   if !lspserver.caps->has_key('typeDefinitionProvider')
                        || !lspserver.caps.typeDefinitionProvider
     return
   endif
 
-  util.PushCursorToTagStack()
+  if !peek
+    util.PushCursorToTagStack()
+  endif
+  lspserver.peekDefDecl = peek
   var req = lspserver.createRequest('textDocument/typeDefinition')
 
   # interface TypeDefinitionParams
 
 # Request: "textDocument/implementation"
 # Param: ImplementationParams
-def s:gotoImplementation(lspserver: dict<any>): void
+def s:gotoImplementation(lspserver: dict<any>, peek: bool): void
   # Check whether LSP server supports jumping to a implementation
   if !lspserver.caps->has_key('implementationProvider')
                        || !lspserver.caps.implementationProvider
     return
   endif
 
-  util.PushCursorToTagStack()
+  if !peek
+    util.PushCursorToTagStack()
+  endif
+  lspserver.peekDefDecl = peek
   var req = lspserver.createRequest('textDocument/implementation')
 
   # interface ImplementationParams
     completionTriggerChars: [],
     diagsMap: {},
     workspaceSymbolPopup: 0,
-    workspaceSymbolQuery: ''
+    workspaceSymbolQuery: '',
+    peekDefDecl: false
   }
   # Add the LSP server functions
   lspserver->extend({
 
 :LspGotoDeclaration    Go to the declaration of the symbol under cursor
 :LspGotoTypeDef                Go to the type definition of the symbol under cursor
 :LspGotoImpl           Go to the implementation of the symbol under cursor
+:LspPeekDefinition     Open the definition of the symbol under cursor in the
+                       preview window.
+:LspPeekDeclaration    Open the declaration of the symbol under cursor in the
+                       preview window.
+:LspPeekTypeDef                Open the type definition of the symbol under cursor in
+                       the preview window.
+:LspPeekImpl           Open the implementation of the symbol under cursor in
+                       the preview window.
 :LspShowSignature      Display the signature of the symbol under cursor.
 :LspDiagShow           Display the diagnostics messages from the LSP server
                        for the current buffer in a location list.
                        |:LspGotoDefinition| command. Note that not all the LSP
                        servers support this feature.
 
+                                               *:LspPeekDefinition*
+:LspPeekDefinition     Displays the line where the symbol under the cursor is
+                       defined in the |preview-window|. The symbol is
+                       highlighted in the preview window.
+
+                                               *:LspPeekDeclaration*
+:LspPeekDeclaration    Displays the line where the symbol under the
+                       cursor is declared in the |preview-window|. The
+                       behavior of this command is similar to the
+                       |:LspPeekDefinition| command.
+
+                                               *:LspPeekTypeDef*
+:LspPeekTypeDef                Displays the line where the type of the symbol under
+                       the cursor is defined in the |preview-window|. The
+                       behavior of this command is similar to the
+                       |:LspPeekDefinition| command. Note that not all the
+                       LSP servers support this feature.
+
+                                               *:LspPeekImpl*
+:LspPeekImpl           Displays the implementation of the symbol under the
+                       cursor in the |preview-window|. The behavior of this
+                       command is similar to the |:LspPeekDefinition|
+                       command. Note that not all the LSP servers support
+                       this feature.
+
                                                *:LspShowSignature*
 :LspShowSignature      Displays the signature of the symbol (e.g. a function
                        or method) before the cursor in a popup.
 
 " LSP commands
 command! -nargs=0 -bar LspShowServers call lsp#showServers()
 command! -nargs=1 -bar LspSetTrace call lsp#setTraceServer(<q-args>)
-command! -nargs=0 -bar LspGotoDefinition call lsp#gotoDefinition()
-command! -nargs=0 -bar LspGotoDeclaration call lsp#gotoDeclaration()
-command! -nargs=0 -bar LspGotoTypeDef call lsp#gotoTypedef()
-command! -nargs=0 -bar LspGotoImpl call lsp#gotoImplementation()
+command! -nargs=0 -bar LspGotoDefinition call lsp#gotoDefinition(v:false)
+command! -nargs=0 -bar LspGotoDeclaration call lsp#gotoDeclaration(v:false)
+command! -nargs=0 -bar LspGotoTypeDef call lsp#gotoTypedef(v:false)
+command! -nargs=0 -bar LspGotoImpl call lsp#gotoImplementation(v:false)
+command! -nargs=0 -bar LspPeekDefinition call lsp#gotoDefinition(v:true)
+command! -nargs=0 -bar LspPeekDeclaration call lsp#gotoDeclaration(v:true)
+command! -nargs=0 -bar LspPeekTypeDef call lsp#gotoTypedef(v:true)
+command! -nargs=0 -bar LspPeekImpl call lsp#gotoImplementation(v:true)
 command! -nargs=0 -bar LspShowSignature call lsp#showSignature()
 command! -nargs=0 -bar LspDiagShow call lsp#showDiagnostics()
 command! -nargs=0 -bar LspDiagCurrent call lsp#showCurrentDiag()
 
 " Add the GUI menu entries
 if has('gui_running')
-  anoremenu <silent> L&sp.Goto.Definition :call lsp#gotoDefinition()<CR>
-  anoremenu <silent> L&sp.Goto.Declaration :call lsp#gotoDeclaration()<CR>
-  anoremenu <silent> L&sp.Goto.Implementation :call lsp#gotoImplementation()<CR>
-  anoremenu <silent> L&sp.Goto.TypeDef :call lsp#gotoTypedef()<CR>
+  anoremenu <silent> L&sp.Goto.Definition :call lsp#gotoDefinition(v:false)<CR>
+  anoremenu <silent> L&sp.Goto.Declaration :call lsp#gotoDeclaration(v:false)<CR>
+  anoremenu <silent> L&sp.Goto.Implementation :call lsp#gotoImplementation(v:false)<CR>
+  anoremenu <silent> L&sp.Goto.TypeDef :call lsp#gotoTypedef(v:false)<CR>
 
   anoremenu <silent> L&sp.Show\ Signature :call lsp#showSignature()<CR>
   anoremenu <silent> L&sp.Show\ References :call lsp#showReferences()<CR>
 
   if &mousemodel =~ 'popup'
     anoremenu <silent> PopUp.L&sp.Go\ to\ Definition
-         \ :call lsp#gotoDefinition()<CR>
+         \ :call lsp#gotoDefinition(v:false)<CR>
     anoremenu <silent> PopUp.L&sp.Go\ to\ Declaration
-         \ :call lsp#gotoDeclaration()<CR>
+         \ :call lsp#gotoDeclaration(v:false)<CR>
     anoremenu <silent> Popup.L&sp.Find\ All\ References
          \ :call lsp#showReferences()<CR>
     anoremenu <silent> PopUp.L&sp.Show\ Detail