Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lsp: Make vim support more explicit, add code completions #5917

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
# limitations under the License.

set(GRAMMAR_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_grammar_tables.py")
set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_vim_syntax.py")
set(VIMSYNTAX_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/vim/generate_syntax.py")
set(XML_REGISTRY_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_registry_tables.py")
set(LANG_HEADER_PROCESSING_SCRIPT "${spirv-tools_SOURCE_DIR}/utils/generate_language_headers.py")

Expand Down
87 changes: 87 additions & 0 deletions utils/vim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Neovim configuration guide for SPIR-V disassembly files

This directory holds instructions to configure Neovim for SPIR-V assembly files (`.spvasm`)

At the end, Neovim should support:
* Syntax highlighting
* Jump to definition
* Find all references
* Symbol renaming
* Operand hover information
* Formatting
* Completion suggestions for all Opcodes and Ids

While the instructions here are specifically for Neovim, they should translate easily to vim.

## Dependencies

In order to build and install the Visual Studio Code language server extension, you will need to install and have on your `PATH` the following dependencies:
* [`golang 1.16+`](https://golang.org/)

## File type detection

Neovim's default config location is typically `~/.config/nvim` so the rest of the instructions assume that but it will need to be changed if your system is different.

Tell neovim that `*.spvasm` files should be treated as `spvasm` filetype
```bash
echo "au BufRead,BufNewFile *.spvasm set filetype=spvasm" > ~/.config/nvim/ftdetect/spvasm.vim
```

## Syntax Highlighting

### Generate the syntax highlighting file
```bash
cd <spirv-tools dir>
mkdir -p build && cd build
# Any platform is fine, ninja is used an as example
cmake -G Ninja ..
ninja spirv-tools-vimsyntax
```

### Copy the syntax file
```bash
cp spvasm.vim ~/.config/nvim/syntax/spvasm.vim
```

## Language Server

### Building the LSP (masOS / Linux)

Run `build_lsp.sh`
Copy `spirvls` and `spirv.json` to a location in `$PATH`

```bash
cd <spirv-tools dir>/utils/vscode
./build_lsp.sh
sudo cp spirvls/* /usr/local/bin/
```

### Building the LSP (Windows)

TODO

### Configuring Neovim

Configuration will depend a lot on your installed plugins but assuming you are using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) the following should be sufficient.

```lua
local lspconfig = require 'lspconfig'
local configs = require 'lspconfig.configs'

if not configs.spvasm then
configs.spvasm = {
default_config = {
cmd = { 'spirvls' },
filetypes = { 'spvasm' },
root_dir = function(fname)
return '.'
end,
settings = {},
},
}
end

lspconfig.spvasm.setup {
capabilities = require('cmp_nvim_lsp').default_capabilities(vim.lsp.protocol.make_client_capabilities()),
}
```
File renamed without changes.
5 changes: 3 additions & 2 deletions utils/vscode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ The extension supports:
* Symbol renaming
* Operand hover information
* Formatting
* Completion suggestions for all Opcodes and Ids

## Dependencies

Expand All @@ -18,8 +19,8 @@ In order to build and install the Visual Studio Code language server extension,

## Installing (macOS / Linux)

Run `install.sh`
Run `install_vscode.sh`

## Installing (Windows)

Run `install.bat`
Run `install_vscode.bat`
28 changes: 28 additions & 0 deletions utils/vscode/build_lsp.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Copyright (c) 2019 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

set -e # Fail on any error.

ROOT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"

pushd ${ROOT_PATH}
go run ./src/tools/gen-grammar.go --cache ./cache --template ./spirv.json.tmpl --out ./spirv.json
go run ./src/tools/gen-grammar.go --cache ./cache --template ./src/schema/schema.go.tmpl --out ./src/schema/schema.go

mkdir -p ./spirvls
cp ./spirv.json ./spirvls

go build -o ./spirvls/spirvls ./src/langsvr.go
popd
File renamed without changes.
File renamed without changes.
138 changes: 114 additions & 24 deletions utils/vscode/spirv.json

Large diffs are not rendered by default.

58 changes: 54 additions & 4 deletions utils/vscode/src/langsvr.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"path"
Expand Down Expand Up @@ -67,7 +66,7 @@ func (s wSpy) Write(p []byte) (n int, err error) {

// main entry point.
func main() {
log.SetOutput(ioutil.Discard)
log.SetOutput(io.Discard)
if enableDebugLogging {
// create a log file in the executable's directory.
if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil {
Expand Down Expand Up @@ -213,6 +212,10 @@ func (s *server) Initialize(ctx context.Context, p *lsp.ParamInitia) (*lsp.Initi
ReferencesProvider: true,
RenameProvider: true,
DocumentFormattingProvider: true,
CompletionProvider: &lsp.CompletionOptions{
TriggerCharacters: []string{"%"},
ResolveProvider: false,
},
},
}
return &res, nil
Expand All @@ -225,9 +228,56 @@ func (s *server) WillSaveWaitUntil(ctx context.Context, p *lsp.WillSaveTextDocum
log.Println("server.WillSaveWaitUntil()")
return nil, nil
}

func markdownOpcode(op *schema.Opcode) string {
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("**%s** (%s)\n\n", op.Opname, op.Class))

for idx, operand := range op.Operands {
sb.WriteString(fmt.Sprintf("Operand %d%s: ", idx, operand.Quantifier))
sb.WriteString(fmt.Sprintf("%s (%s)\n", operand.Name, operand.Kind.Kind))
}

return sb.String()
}

func (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) {
log.Println("server.Completion()")
return nil, nil
f := s.getFile(p.TextDocument.URI)
if f == nil {
return nil, fmt.Errorf("Unknown file")
}

if p.Context.TriggerCharacter == "%" {
idents := []lsp.CompletionItem{}
for name, ident := range f.res.Identifiers {
idents = append(idents, lsp.CompletionItem{
Label: name,
Kind: 6,
Documentation: ident.Definition.Range.Text(f.res.Lines),
})
}
res := &lsp.CompletionList{
IsIncomplete: false,
Items: idents,
}

return res, nil
}

opcodes := []lsp.CompletionItem{}
for name, opcode := range schema.Opcodes {
opcodes = append(opcodes, lsp.CompletionItem{
Label: name,
Kind: 3,
Documentation: markdownOpcode(opcode),
})
}
res := &lsp.CompletionList{
IsIncomplete: false,
Items: opcodes,
}
return res, nil
}
func (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) {
log.Println("server.Resolve()")
Expand All @@ -246,7 +296,7 @@ func (s *server) Hover(ctx context.Context, p *lsp.HoverParams) (*lsp.Hover, err
default:
sb.WriteString(fmt.Sprintf("<Unhandled type '%T'>", v))
case *parser.Instruction:
sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname))
sb.WriteString(markdownOpcode(v.Opcode))
case *parser.Identifier:
sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines)))
case *parser.Operand:
Expand Down
Loading