Skip to content

Commit

Permalink
add Vue file support
Browse files Browse the repository at this point in the history
if you change the type of your .vue files to coffeescript, this  extension will now extract the `<script lang="coffee"/>` section internally and provide autocomplete for it. Doesn't work with vetur/volar anymore then, though, because only one can be active at the same time. For current status on integrating both together, follow vuejs/language-tools#3200
  • Loading branch information
phil294 committed Aug 7, 2023
1 parent 2e32697 commit 68c0c30
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 19 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -55,6 +55,8 @@ You can **install the extension in VSCode from [HERE](https://marketplace.visual
- [ ] *missing* Rename file
- [ ] *missing* Syntactic folding ranges

If you're using *Vue.js*, you can You can also get coffee language support inside `.vue` single file component files by adding this to your settings: `"files.associations": { "*.vue": "coffeescript" }`. If your file contains a `<script lang="coffee">` section, it will work as expected. This feature disables any other Vue extensions like Vetur/Volar though, so activate with caution.

### Setup

The following VSCode extension options are available. The default values are set.
Expand Down
31 changes: 24 additions & 7 deletions server/src/embeddedSupport/coffeescriptDocumentRegionParser.ts
Expand Up @@ -11,15 +11,32 @@ export interface EmbeddedRegion {
}

export function parseCoffeescriptDocumentRegions(document: TextDocument) {
let regions: EmbeddedRegion[] = []
const text = document.getText();
const regions: EmbeddedRegion[] = [
{
languageId: 'javascript',
start: 0,
end: text.length,
type: 'script'
if(document.uri.endsWith('.vue')) {
const vueMatch = text.match(/(.*)(<script\s+lang=["']coffee(?:script)?["']\s*>\s*)(.+?)(\s*<\/script\s*>)(.*)/si)
if(vueMatch) {
regions = [
{
languageId: 'javascript',
start: vueMatch[1]!.length + vueMatch[2]!.length,
end: vueMatch[1]!.length + vueMatch[2]!.length + vueMatch[3]!.length,
type: 'script'
}
];
}
];
}
if(!regions.length) {
regions = [
{
// TODO: why javascript? shouldn't this be LANGUAGE_ID aka coffeescript?
languageId: 'javascript',
start: 0,
end: text.length,
type: 'script'
}
];
}
return {
importedScripts: [],
regions
Expand Down
4 changes: 2 additions & 2 deletions server/src/embeddedSupport/embeddedSupport.ts
Expand Up @@ -95,7 +95,7 @@ export function getSingleTypeDocument(
const oldContent = document.getText();
let newContent = oldContent
.split('\n')
.map(line => ' '.repeat(line.length))
.map(line => line.length > 0 ? '#' + ' '.repeat(line.length - 1) : '')
.join('\n');

let langId = defaultLanguageIdForBlockTypes[type];
Expand All @@ -109,7 +109,7 @@ export function getSingleTypeDocument(
// newContent is coffee

try {
newContent = transpile_service.transpile(document).js || (()=>{throw new Error(`no js set for ${document.uri}`)})()
newContent = transpile_service.transpile(document, newContent).js || (()=>{throw new Error(`no js set for ${document.uri}`)})()
} catch(e: any) {
logger.logInfo('TRANSPILATION FAILED ' + document.uri + ' ' + JSON.stringify(e) + e.stack)
}
Expand Down
18 changes: 9 additions & 9 deletions server/src/services/transpileService.ts
Expand Up @@ -65,7 +65,7 @@ interface ITranspilationResult {

interface ITranspileService {
result_by_uri: Map<string, ITranspilationResult>,
transpile(coffee_doc: TextDocument): ITranspilationResult,
transpile(coffee_doc: TextDocument, coffee: string): ITranspilationResult,
position_js_to_coffee(result: ITranspilationResult, js_position: Position, coffee_doc: TextDocument): Position | undefined,
range_js_to_coffee(result: ITranspilationResult, js_range: Range, coffee_doc: TextDocument): Range | undefined,
position_coffee_to_js(result: ITranspilationResult, coffee_position: Position, coffee_doc: TextDocument): Position | undefined,
Expand All @@ -77,8 +77,8 @@ const transpilation_cache: Map<string,ITranspilationResult> = new VolatileMap(18

/** The resulting coffee must still always be valid and parsable by the compiler,
and should not shift characters around much (otherwise source maps would need changes too) */
function preprocess_coffee(coffee_doc: TextDocument) {
const tmp = (coffee_doc.getText() as string)
function preprocess_coffee(coffee_doc: TextDocument, old_coffee: string) {
const tmp = old_coffee
// Enable autocomplete at `@|`. Replace with magic snippet that allows for both @|
// and standalone @ sign. Cursor needs to be adjusted properly in doComplete().
// .____CoffeeSenseAtSign is replaced with (this.valueOf(),this) in postprocess_js.
Expand Down Expand Up @@ -167,9 +167,9 @@ function preprocess_coffee(coffee_doc: TextDocument) {
object_tweak_coffee_lines.push(line_i)
}
})
const coffee = tmp_lines.join('\n')
const new_coffee = tmp_lines.join('\n')
const inserted_coffee_lines = starts_with_block_comment_lines
return { coffee, inserted_coffee_lines, object_tweak_coffee_lines }
return { coffee: new_coffee, inserted_coffee_lines, object_tweak_coffee_lines }
}

/** further transforms that *can break* cs compilation, to be used if compilation could not succeed without it anyway */
Expand Down Expand Up @@ -624,16 +624,16 @@ const transpile_service: ITranspileService = {

result_by_uri: new Map(),

transpile(orig_coffee_doc) {
const hash = MD5.hex(orig_coffee_doc.getText())
transpile(orig_coffee_doc, coffee) {
const hash = MD5.hex(coffee)
const cached = transpilation_cache.get(hash)
if (cached) {
logger.logDebug(`found cached compilation for contents of ${orig_coffee_doc.uri}`)
this.result_by_uri.set(orig_coffee_doc.uri, cached)
return cached
}

const { coffee: preprocessed_coffee, object_tweak_coffee_lines, inserted_coffee_lines } = preprocess_coffee(orig_coffee_doc)
const { coffee: preprocessed_coffee, object_tweak_coffee_lines, inserted_coffee_lines } = preprocess_coffee(orig_coffee_doc, coffee)
// As coffee was modified, offsets and positions are changed and for these purposes,
// we need to construct a new doc
let mod_coffee_doc = TextDocument.create(orig_coffee_doc.uri, 'coffeescript', 1, preprocessed_coffee)
Expand All @@ -650,7 +650,7 @@ const transpile_service: ITranspileService = {
} else {
// Nothing worked at all. As a last resort, just pass the coffee to tsserver,
// with minimal transforms:
result.js = pseudo_compile_coffee(orig_coffee_doc.getText())
result.js = pseudo_compile_coffee(coffee)
}

transpilation_cache.set(hash, result)
Expand Down
13 changes: 13 additions & 0 deletions test/lsp/features/diagnostics/basic.test.ts
Expand Up @@ -83,4 +83,17 @@ describe('Should find diagnostics', () => {
message: string_to_number_error
})), true)
})

it('can work with Vue files', async () => {
// see test/lsp/fixture/.vscode/settings.json
const docUri = getDocUri('diagnostics/vue-file.vue')
// vue parsing demonstrated by returning ts error
await testDiagnostics(docUri, [
{
range: sameLineRange(14, 0, 15),
severity: vscode.DiagnosticSeverity.Error,
message: string_to_number_error
}
])
})
})
3 changes: 2 additions & 1 deletion test/lsp/fixture/.vscode/settings.json
@@ -1,6 +1,7 @@
{
"files.associations": {
"*.something-else-than-coffee": "coffeescript"
"*.something-else-than-coffee": "coffeescript",
"*.vue": "coffeescript"
},
"coffeesense.dev.logLevel": "DEBUG"
}
22 changes: 22 additions & 0 deletions test/lsp/fixture/diagnostics/vue-file.vue
@@ -0,0 +1,22 @@
<template lang="pug">
div.markdown.column(v-drop="on_drop")
.fakeinput(role="button")
| 123
</template>

<template>
<div></div>
</template>

<scrIPT lang="COFFEESCRipt" >
# @ts-check
vue_success_var = 123
vue_success_var = '123'
</script>
<style>
document {
display: none;
}
</style>

0 comments on commit 68c0c30

Please sign in to comment.