Skip to content

Commit

Permalink
implemented editor features for js/ts content in .blits file
Browse files Browse the repository at this point in the history
  • Loading branch information
uguraslan committed Sep 25, 2024
1 parent d7028ff commit 2ef5bbf
Show file tree
Hide file tree
Showing 13 changed files with 866 additions and 235 deletions.
66 changes: 66 additions & 0 deletions src/blitsFile/codeActionsProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const vscode = require('vscode')
const { getVirtualFileName } = require('./utils/fileNameGenerator')
const { getLanguageServiceInstance } = require('./languageService')
const { extractScriptContent } = require('./utils/scriptExtractor')

function registerCodeActionsProvider(context) {
const codeActionProvider = vscode.languages.registerCodeActionsProvider(
'blits',
{
provideCodeActions(document, range) {
const scriptInfo = extractScriptContent(document.getText())
if (!scriptInfo) return

const startOffset = document.offsetAt(range.start) - scriptInfo.startIndex
const endOffset = document.offsetAt(range.end) - scriptInfo.startIndex
if (startOffset < 0 || endOffset < 0) return

const virtualFileName = getVirtualFileName(document.uri, scriptInfo.lang)
const languageService = getLanguageServiceInstance()
const diagnostics = languageService
.getSemanticDiagnostics(virtualFileName)
.filter((d) => d.start >= startOffset && d.start + d.length <= endOffset)

const actions = diagnostics
.map((diagnostic) => {
if (diagnostic.code === 7027) {
// TS7027: Unreachable code detected
const fix = new vscode.CodeAction('Remove unreachable code', vscode.CodeActionKind.QuickFix)
const startPos = document.positionAt(scriptInfo.startIndex + diagnostic.start)
const endPos = document.positionAt(scriptInfo.startIndex + diagnostic.start + diagnostic.length)
fix.edit = new vscode.WorkspaceEdit()
fix.edit.delete(document.uri, new vscode.Range(startPos, endPos))
return fix
}
return null
})
.filter((action) => action !== null)

return actions
},
},
{
providedCodeActionKinds: [vscode.CodeActionKind.QuickFix],
}
)
context.subscriptions.push(codeActionProvider)
}

module.exports = { registerCodeActionsProvider }
77 changes: 77 additions & 0 deletions src/blitsFile/completionProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const vscode = require('vscode')
const { getVirtualFileName } = require('./utils/fileNameGenerator')
const { getLanguageServiceInstance } = require('./languageService')
const { extractScriptContent } = require('./utils/scriptExtractor')

function capitalizeFirstLetter(str) {
if (!str) return ''
return str.charAt(0).toUpperCase() + str.slice(1)
}

function registerCompletionProvider(context) {
const completionProvider = vscode.languages.registerCompletionItemProvider(
'blits',
{
provideCompletionItems(document, position) {
const scriptInfo = extractScriptContent(document.getText())
if (!scriptInfo) return

const offset = document.offsetAt(position) - scriptInfo.startIndex
if (offset < 0) return

const virtualFileName = getVirtualFileName(document.uri, scriptInfo.lang)
const { getLanguageService } = getLanguageServiceInstance()
const languageService = getLanguageService(virtualFileName)

if (!languageService) return

const completions = languageService.getCompletionsAtPosition(virtualFileName, offset, {
includeCompletionsWithInsertText: true,
})

if (!completions) return

return completions.entries.map((entry) => {
// Normalize the kind name to match VSCode's CompletionItemKind keys
const kindName = capitalizeFirstLetter(entry.kind.toLowerCase())
const mappedKind = vscode.CompletionItemKind[kindName]

/**
* @type {vscode.CompletionItemKind}
*/
const kind = typeof mappedKind === 'number' ? mappedKind : vscode.CompletionItemKind.Variable

const item = new vscode.CompletionItem(entry.name, kind)
item.detail = entry.kind
if (entry.kindModifiers) {
item.detail += ` (${entry.kindModifiers})`
}
return item
})
},
},
'.', // Trigger characters
'(',
'@' // Trigger characters
)
context.subscriptions.push(completionProvider)
}

module.exports = { registerCompletionProvider }
51 changes: 51 additions & 0 deletions src/blitsFile/config/compilerOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const ts = require('typescript')

// Base compiler options applicable to both JS and TS
const baseCompilerOptions = {
target: ts.ScriptTarget.ES2020,
module: ts.ModuleKind.ES2020,
moduleResolution: ts.ModuleResolutionKind.NodeNext,
esModuleInterop: true,
noEmit: true,
baseUrl: '.',
paths: {
'*': ['node_modules/*'],
},
include: ['blits.d.ts', '**/*.ts', '**/*.js', '**/*.blits'],
allowArbitraryExtensions: true,
allowNonTsExtensions: true,
}

// JavaScript-specific compiler options
const jsCompilerOptions = {
...baseCompilerOptions,
allowJs: true,
checkJs: true,
}

// TypeScript-specific compiler options
const tsCompilerOptions = {
...baseCompilerOptions,
noUnusedLocals: true,
noUnusedParameters: true,
noImplicitReturns: false,
}

module.exports = { jsCompilerOptions, tsCompilerOptions, baseCompilerOptions }
107 changes: 107 additions & 0 deletions src/blitsFile/diagnostics.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const vscode = require('vscode')
const ts = require('typescript')
const { extractScriptContent } = require('./utils/scriptExtractor')
const { getVirtualFileName } = require('./utils/fileNameGenerator')
const { getLanguageServiceInstance } = require('./languageService')
const { addVirtualFile, deleteVirtualFilesByUri } = require('./virtualDocuments')

const diagnosticCollection = vscode.languages.createDiagnosticCollection('blits')

function registerDiagnostics(context) {
context.subscriptions.push(diagnosticCollection)

const updateDiagnostics = (document) => {
const scriptInfo = extractScriptContent(document.getText())
if (!scriptInfo) {
diagnosticCollection.set(document.uri, [])
return
}

const virtualFileName = getVirtualFileName(document.uri, scriptInfo.lang)
addVirtualFile(virtualFileName, scriptInfo.content, document.version)

const { getLanguageService } = getLanguageServiceInstance()
const languageService = getLanguageService(virtualFileName)

if (!languageService) {
diagnosticCollection.set(document.uri, [])
return
}

const syntacticDiagnostics = languageService.getSyntacticDiagnostics(virtualFileName)
const semanticDiagnostics = languageService.getSemanticDiagnostics(virtualFileName)
const allDiagnostics = syntacticDiagnostics.concat(semanticDiagnostics)

const diagnostics = allDiagnostics
.map((diagnostic) => {
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')
if (diagnostic.file) {
const start = scriptInfo.startIndex + diagnostic.start
const end =
diagnostic.start !== undefined && diagnostic.length !== undefined ? start + diagnostic.length : start + 1 // Fallback if length is undefined

const startPos = document.positionAt(start)
const endPos = document.positionAt(end)

return new vscode.Diagnostic(
new vscode.Range(startPos, endPos),
message,
diagnostic.category === ts.DiagnosticCategory.Error
? vscode.DiagnosticSeverity.Error
: vscode.DiagnosticSeverity.Warning
)
}
return null
})
.filter((d) => d !== null)

diagnosticCollection.set(document.uri, diagnostics)
}

// Event listeners
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((event) => {
if (event.document.languageId === 'blits') {
updateDiagnostics(event.document)
}
}),
vscode.workspace.onDidOpenTextDocument((document) => {
if (document.languageId === 'blits') {
updateDiagnostics(document)
}
}),
vscode.workspace.onDidCloseTextDocument((document) => {
if (document.languageId === 'blits') {
deleteVirtualFilesByUri(document.uri)
diagnosticCollection.delete(document.uri)
}
})
)

// Initialize diagnostics for active editor
if (vscode.window.activeTextEditor) {
const document = vscode.window.activeTextEditor.document
if (document.languageId === 'blits') {
updateDiagnostics(document)
}
}
}

module.exports = { registerDiagnostics }
53 changes: 53 additions & 0 deletions src/blitsFile/hoverProvider.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2023 Comcast Cable Communications Management, LLC
* 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.
*
* SPDX-License-Identifier: Apache-2.0
*/

const vscode = require('vscode')
const { getVirtualFileName } = require('./utils/fileNameGenerator')
const { getLanguageServiceInstance } = require('./languageService')
const { extractScriptContent } = require('./utils/scriptExtractor')

function registerHoverProvider(context) {
const hoverProvider = vscode.languages.registerHoverProvider('blits', {
provideHover(document, position) {
const scriptInfo = extractScriptContent(document.getText())
if (!scriptInfo) return

const offset = document.offsetAt(position) - scriptInfo.startIndex
if (offset < 0) return

const virtualFileName = getVirtualFileName(document.uri, scriptInfo.lang)
const { getLanguageService } = getLanguageServiceInstance()
const languageService = getLanguageService(virtualFileName)

if (!languageService) return

const hoverInfo = languageService.getQuickInfoAtPosition(virtualFileName, offset)

if (hoverInfo && hoverInfo.displayParts) {
const contents = hoverInfo.displayParts.map((part) => part.text).join('')
const markdown = new vscode.MarkdownString()
markdown.appendCodeblock(contents, scriptInfo.lang === 'ts' ? 'typescript' : 'javascript')

const range = document.getWordRangeAtPosition(position)
return new vscode.Hover(markdown, range)
}
},
})
context.subscriptions.push(hoverProvider)
}

module.exports = { registerHoverProvider }
Loading

0 comments on commit 2ef5bbf

Please sign in to comment.