/******************************************************************************
 * Copyright 2021 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/ import * as vscode from 'vscode' ; import * as path from 'path' ; import { LanguageClient , LanguageClientOptions , ServerOptions , TransportKind } from 'vscode-languageclient/node' ; let client ; export function activate ( context ) { client = startLanguageClient ( context ) ; configureTemplateDecoration ( context ) ; } export function deactivate ( ) { if ( client ) { return client . stop ( ) ; } return undefined ; } function startLanguageClient ( context ) { const serverModule = context . asAbsolutePath ( path . join ( 'out' , 'language-server' , 'main' ) ) ; const debugOptions = { execArgv : [ '--nolazy' , `--inspect${ process . env . DEBUG_BREAK ? '-brk' : '' }=${ process . env . DEBUG_SOCKET || '6009' }` ] } ; const serverOptions = { run : { module : serverModule , transport : TransportKind . ipc } , debug : { module : serverModule , transport : TransportKind . ipc , options : debugOptions } } ; const fileSystemWatcher = vscode . workspace . createFileSystemWatcher ( '**/*.langium' ) ; context . subscriptions . push ( fileSystemWatcher ) ; const clientOptions = { documentSelector : [ { scheme : 'file' , language : 'langium' } ] , synchronize : { fileEvents : fileSystemWatcher } } ; const client = new LanguageClient ( 'langium' , 'Langium' , serverOptions , clientOptions ) ; client . start ( ) ; return client ; } const DELAY = 100 ; function configureTemplateDecoration ( context ) { const decorationType = vscode . window . createTextEditorDecorationType ( decorationRenderOptions ( ) ) ; const decorator = cancelableDecorator ( ( editor ) => editor . setDecorations ( decorationType , createDecorations ( editor . document ) ) ) ; decorator ( ) ; vscode . window . onDidChangeActiveTextEditor ( ( ) => decorator ( ) , null , context . subscriptions ) ; vscode . workspace . onDidChangeTextDocument ( ( ) => decorator ( ) , null , context . subscriptions ) ; } type Decorator = ( editor : vscode . TextEditor ) => void ; type CancelableDecorator = ( ) => void ; type TimeoutID = ReturnType < typeof setTimeout > ; function cancelableDecorator ( decorator , delay = 500 ) { let timeout ; return ( ) => { const editor = vscode . window . activeTextEditor ; timeout && clearTimeout ( timeout ) ; timeout = editor && setTimeout ( ( ) => decorator ( editor ) , DELAY ) ; } ; } function createDecorations ( document ) { const regEx = /(\S+)`([^`]*)`/g ; const text = document . getText ( ) ; const decorations = [ ] ; let match ; while ( ( match = regEx . exec ( text ) ) ) { const name = match [ 1 ] ; const nameIndex = match . index ; if ( isSmartTemplate ( name , nameIndex ) ) { const text = match [ 2 ] ; const textIndex = nameIndex + name . length + 1 ; decorateText ( document , decorations , text , textIndex ) ; } } return decorations ; } function decorateText ( document , decorations , text , textIndex ) { const indentation = findIndentation ( text ) ; const regEx = /[^\r?\n]+/g ; let match ; while ( ( match = regEx . exec ( text ) ) ) { const startIndex = textIndex + match . index ; const startPos = document . positionAt ( startIndex + indentation ) ; const endPos = document . positionAt ( startIndex + match [ 0 ] . trimRight ( ) . length ) ; if ( startPos . isBefore ( endPos ) ) { const range = new vscode . Range ( startPos , endPos ) ; decorations . push ( { range } ) ; } } } function findIndentation ( text ) { const indents = text . split ( /[\r?\n]/g ) . map ( line => line . trimRight ( ) ) . filter ( line => line . length > 0 ) . map ( line => line . search ( /\S|$/ ) ) ; const min = indents . length === 0 ? 0 : Math . min ( ... indents ) ; return Math . max ( 0 , min ) ; } function isSmartTemplate ( name , index ) { return name === "indent" && index !== - 1 ; } function decorationRenderOptions ( ) { return { overviewRulerLane : vscode . OverviewRulerLane . Right , light : { backgroundColor : 'rgba(0, 0, 0, 0.13)' , overviewRulerColor : 'rgba(0, 0, 0, 0.13)' } , dark : { backgroundColor : 'rgba(255, 255, 255, 0.13)' , overviewRulerColor : 'rgba(255, 255, 255, 0.13)' } } ; }