import { CancellationToken , TextDocument , Position , Hover } from 'vscode' ; import * as fs from 'fs' ; import * as vscode from 'vscode' ; import { isPositionInString , intrinsics , FORTRAN_KEYWORDS } from '../lib/helper' ; import { getDeclaredFunctions } from '../lib/functions' ; import { EXTENSION_ID } from '../lib/tools' ; import { LoggingService } from '../services/logging-service' ; class CaseCoverter { preferredCase : string ; static LOWER = 'lowercase' ; static UPPER = 'uppercase' ; constructor ( preferredCase = CaseCoverter . LOWER ) { this . preferredCase = preferredCase ; } convert ( word ) { if ( this . preferredCase === CaseCoverter . LOWER ) { return this . toLower ( word ) ; } else if ( this . preferredCase === CaseCoverter . UPPER ) { return this . toUpper ( word ) ; } throw new Error ( `the provided case ${ this . preferredCase } is not supported` ) ; } toLower ( word ) { return word . toLowerCase ( ) ; } toUpper ( word ) { return word . toUpperCase ( ) ; } } export class FortranCompletionProvider implements vscode . CompletionItemProvider { constructor ( private loggingService ) { } public provideCompletionItems ( document , position , token ) { return this . provideCompletionItemsInternal ( document , position , token , vscode . workspace . getConfiguration ( EXTENSION_ID ) ) ; } public provideCompletionItemsInternal ( document , position , token , config ) { return new Promise < vscode . CompletionItem [ ] > ( ( resolve , reject ) => { const lineText = document . lineAt ( position . line ) . text ; const lineTillCurrentPosition = lineText . substr ( 0 , position . character ) ; if ( lineText . match ( /^\s*\/\// ) ) { return resolve ( [ ] ) ; } const inString = isPositionInString ( document , position ) ; if ( ! inString && lineTillCurrentPosition . endsWith ( '"' ) ) { return resolve ( [ ] ) ; } const currentWord = this . getCurrentWord ( document , position ) ; if ( currentWord . match ( /^\d+$/ ) ) { return resolve ( [ ] ) ; } const caseConverter = new CaseCoverter ( config . get ( 'preferredCase' ) ) ; if ( currentWord . length === 0 ) { resolve ( [ ] ) ; } const intrinsicSuggestions = this . getIntrinsicSuggestions ( currentWord , caseConverter ) ; const keywordSuggestions = this . getKeywordSuggestions ( currentWord ) ; const functionSuggestions = this . getFunctionSuggestions ( document , currentWord ) ; return resolve ( [ ... intrinsicSuggestions , ... keywordSuggestions , ... functionSuggestions ] ) ; } ) ; } private getIntrinsicSuggestions ( currentWord , caseConverter ) { return intrinsics . filter ( i => i . startsWith ( currentWord . toUpperCase ( ) ) ) . map ( ( intrinsic ) => { return new vscode . CompletionItem ( caseConverter . convert ( intrinsic ) , vscode . CompletionItemKind . Method ) ; } ) ; } private getKeywordSuggestions ( currentWord ) { return FORTRAN_KEYWORDS . filter ( keyword => keyword . startsWith ( currentWord . toUpperCase ( ) ) ) . map ( keyword => { return new vscode . CompletionItem ( keyword . toLowerCase ( ) , vscode . CompletionItemKind . Keyword ) ; } ) ; } private getFunctionSuggestions ( document , currentWord ) { const functions = getDeclaredFunctions ( document ) ; return functions . filter ( fun => fun . name . startsWith ( currentWord ) ) . map ( fun => { return new vscode . CompletionItem ( `${ fun . name }(` , vscode . CompletionItemKind . Function ) ; } ) ; } private getCurrentWord ( document , position ) { const wordAtPosition = document . getWordRangeAtPosition ( position ) ; let currentWord = '' ; if ( wordAtPosition && wordAtPosition . start . character < position . character ) { const word = document . getText ( wordAtPosition ) ; currentWord = word . substr ( 0 , position . character - wordAtPosition . start . character ) ; } return currentWord ; } }