Skip to Content
Event Information

Annotated links: Episode 19 of Hands-on SAP dev with qmacro

This is a searchable description of the content of a live stream recording, specifically “Episode 19 – Some fun with CAP related tooling” in the “Hands-on SAP dev with qmacro” series. There are links directly to specific highlights in the video recording. For links to annotations of other episodes, please see the “Catch the replays” section of the series blog post.

This episode, titled “Some fun with CAP related tooling“, was streamed live on Wed 17 Apr 2019 and is approximately one hour in length. The stream recording is available on YouTube.

Below is a brief synopsis, and links to specific highlights – use these links to jump directly to particular places of interest in the recording, based on ‘hh:mm:ss’ style timestamps.

Brief synopsis

This being a midweek episode, we allow ourselves a little bit flexibility and look into developer tooling; we look a bit more at syntax highlighting in Vim, some more LSP usage and try to come up with a workflow for setting up to answer CAP questions on the SAP Community Q&A site.

00:05:00: Highlighting another Twitch live streamer (a “twitcher”?) – Suz Hinton, aka noopkat, from whom I’ve learnt an awful lot about live streaming, especially in the early days. JavaScript, IoT and more. Follow Suz for some really interesting streams!

00:06:35: Just a reminder that we have a #handsonsapdev channel in the SAP Mentors and Friends Slack workspace – I recommend you join this workspace (via this form and head on over to the channel to say hi!

00:07:40: Pointing out the CAP space on the Community Q&A where we can and should ask questions and (if we can) answer them too, to build up a body of knowledge there.

00:11:45: Looking at what I’ve been using so far to connect to the CDS language server (via the @sap/cds-lsp package that is delivered inside of the VS Code extension for CDS, available from the cloud section of the SAP Developer Tools site), which is the LanguageClient-neovim plugin for Vim.

This operated in a synchronous fashion, which meant that I got syntax issues highlighted for example only when I saved changes to my CDS sources.

Since then I found the Asynchronous Lint Engine (ALE) which provides all sorts of linting connectivity plus works very well as a Language Server Protocol (LSP) client!

00:14:00: Looking at what NPM packages I have installed globally on my workstation:

=> npm list -g --depth=0
/Users/i347491/.nvm/versions/node/v8.12.0/lib
├── @sap/cds@3.7.1
├── csvf@1.0.0 -> /Users/i347491/local/projects/csvf
├── npm@6.9.0
├── typescript@3.4.3
└── typescript-language-server@0.3.7

which shows I’ve installed TypeScript tools and the TypeScript LSP server. This combination supports JavaScript too, which we look at briefly in a test file in Vim, exploring standard LSP features (beyond syntax error highlighting) such as “go-to-definition” and “find-references”.

00:16:35: So ALE is a plugin I’ve installed in my Vim setup, and we see that it has a number of linter configurations built in, in the ale_linters/ directory. For example, looking in the ale_linters/javascript/tsserver.vim file we see how it bootstraps and connects to the TypeScript LSP server for language services.

So can we use ALE with the @sap/cds-lsp implementation to get CDS language services in Vim, asynchronously? Turns out the answer is yes!

00:20:02: Creating a new project for a test Vim plugin, that we call vim-cds (and getting frustrated in my inconsistent use of ‘folder’ and ‘directory’ – see this Twitter poll for what others thing). In here we create an ftdetect directory that we can use to put some code to work out what file type we’re dealing with when we load files with a cds extension:

au BufNewFile,BufRead *.cds set filetype=cds

00:24:05: Adding this vim-cds/ directory to Vim configuration so it’s used as a plugin, we can see that we now have Vim recognising CDS files. So far so good.

00:25:55: Adding another directory called syntax/, also containing a cds.vim file for syntax highlighting code, which is usually bookended like this:

if (exists "b:current_syntax")
  finish
endif

[...]

let b:current_syntax = "cds"

I learnt this (and the rest of the stuff about syntax highlighting in Vim) from the Basic Syntax Highlighting chapter of the excellent online resource Learn Vimscript the Hard Way by Steve Losh.

00:28:00: Over the course of this next section we build up the “pairs” of syntax highlighting definitions (which you can learn about in the resource referenced above) – basically one defines how to match certain sections of (CDS) code and identifies those matched sections with labels, then one defines how each labelled section should be highlighted using an convention set of group name abstractions (such as Comment, Function, Keyword etc).

To test new syntax highlighting definitions in a sample CDS file, we used the command syn off | syn on to restart syntax highlighting in Vim.

00:33:10: I had looked inside the VS Code extension for CDS (remember, the vsix extension is just a compressed tarball in disguise) to find out how various sections of the CDS language had been defined. Specifically, there’s a file syntaxes/cds.tmLanguage that identifies various keywords, how they appear, and what they are. This file extension and format (tmLanguage) comes from that (now-classic) editor TextMate and is used in VS Code.

If you look at the definitions, you’ll see that the identifications of different keyword groups are named – meta.controld.yield.cds, keyword.strong.control.cds and support.class.cds are examples of these.

00:37:40: These names are available in VS Code for you to look at – at this point we turn on a Developer Mode feature “Inspect TM Scopes” to see what scope each CDS keyword is in. Wonderful!

00:41:00: Turning our attention now to the mechanism we need to get the CDS language server working via ALE.

00:42:10: Looking inside the ale_linters/ directory in the ALE plugin to create a new directory cds/ to put what we need in there. Note that this is just temporary, we don’t really want to be modifying another plugin, but for now, just to get things working, this will do.

Inside this new cds/ directory we need some Vimscript to bootstrap and connect to the CDS language server via the @sap/cds-lsp package, but we use a symbolic link to this Vimscript file which we create as cds.vim inside the vim-cds/ plugin directory.

Here’s an overview of what we’ve got:

.vim/                                  local/
 |                                      |
 +- bundle/                             +- projects/
 |   |                    symbolic          |
 |   +- vim-cds/  ----------------------->  +- vim-cds/
 |   |                      link                |
 |   +- ale/                                    +- cds-lsp/    (extracted from the vsix file)
 |       |                              +---->  +- cds.vim
 |       +- ale_linters/                |       +- ftdetect/
 |           |                          |       |   |
 |           +- cds/                    |       |   +- cds.vim
 |               |             symbolic |       |
 |               +- cds.vim ------------+       +- syntax/
 |                               link           |   |
 +- vimrc                                       |   +- cds.vim
                                                |
                                                +- startcdslsp

00:45:40: Looking at the contents of cds.vim, which starts up the language server in a way that ALE can connect to and make use of it:

" Description: Simple config for using cds-lsp with ALE

call ale#Set('cds_cds_executable', $HOME . '/local/projects/vim-cds/startcdslsp')

function! ale_linters#cds#cds#GetProjectRoot(buffer) abort
    let l:project_file = ale#path#FindNearestFile(a:buffer, '.cdsrc.json')

    return fnamemodify(l:project_file, ':h')
endfunction

function! ale_linters#cds#cds#GetCommand(buffer) abort
    let l:executable = ale#Var(a:buffer, 'cds_cds_executable')

    return l:executable
endfunction

call ale#linter#Define('cds', {
\   'name': 'cds',
\   'lsp': 'stdio',
\   'command': function('ale_linters#cds#cds#GetCommand'),
\   'executable': {b -> ale#Var(b, 'cds_cds_executable')},
\   'project_root': function('ale_linters#cds#cds#GetProjectRoot'),
\})

This uses a little starter script startcdslsp which looks like this:

#!/bin/bash

# Simple bootstrap script to start the CDS LSP server in STDIO mode.
# It assumes that the cds-lsp package directory is in the same directory
# as this script itself, for example:
#
# <dir>/
#   |
#   +-- cds-lsp/
#   +-- startcdslsp

# Get the full name of the directory this script is in
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" > /dev/null 2>&1 && pwd)"

# Start the server
node "${DIR}/cds-lsp/out/src/server.js" --stdio

00:47:40: At this point we need to run npm install inside the cds-lsp/ directory that we took out of the vsix file, to bring in the packages that it needs, including the important one, @sap/cds-compiler!

00:50:10: Time to try things out. Opening up a sample CDS file, we see that we get syntax highlighting, and an error is indicated in the gutter (or the “sign column”). Great!

We see the detail of the error at the bottom of the buffer too.

00:52:00: Adding the final touches for this initial foray with ALE, by adding some configuration to vimrc:

set signcolumn=yes
let g:ale_completion_enabled = 1
let g:ale_sign_column_always = 1
let g:airline#extension#ale#enabled = 1
let g:ale_open_list = 1

nmap <silent><leader>j :lnext<cr>
nmap <silent><leader>k :lprevious<cr>

This sets some options as follows:

  • always show the sign column (the gutter)
  • enable language server powered command completion
  • show indication of errors in my bottom “airline” bar
  • automatically open the problems list when there are issues

It also allows me to navigate up and down errors in the problems list (actually it’s the “location list” in Vim, hence the “l” prefix) with a couple of key mappings in normal mode.

00:57:20: We see that everything is working – if we make a mistake now, we have a similar experience as we see in VS Code, in that the error is highlighted on the actual line, and it also is detailed in a problem list at the bottom, where we can navigate from problem to problem. Hurray!

2 Comments
You must be Logged on to comment or reply to a post.