Skip to content

Commit

Permalink
add svelte lexer (#1979)
Browse files Browse the repository at this point in the history
* add svelte lexer

* remove trailing newline

* remove unneeded comment

Co-authored-by: Tan Le <[email protected]>

* remove excessive newlines

Co-authored-by: Tan Le <[email protected]>

* formatting

* Add guessing specs for Svelte lexer

* Ensure HTML is loaded as part of Svelte lexer

* Add Svelte to the list of supported languages

---------

Co-authored-by: Brodie Davis <[email protected]>
Co-authored-by: Tan Le <[email protected]>
  • Loading branch information
3 people authored Sep 22, 2023
1 parent 17b3aef commit b584ea1
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/Languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
- Stan (`stan`)
- Stata (`stata`)
- SuperCollider (`supercollider`)
- Svelte (`svelte`)
- Swift (`swift`)
- Systemd (`systemd`)
- Syzlang (`syzlang`)
Expand Down
29 changes: 29 additions & 0 deletions lib/rouge/demos/svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!-- This file is made up from several examples on https://svelte.dev/examples and is not expected to function. -->
<script lang="ts">
import Image from './Image.svelte';

let src: string = '/tutorial/image.gif';
let count: number = 1;
$: doubled = count * 2; // the `$:` is special in svelte
</script>

<Image {src} bind:alt="{name.capitalize()} dancing" user={name.toUpperCase(false, 42, {key: 'value'})}
tooltip="I'm helping" false text=asdf on:message={handleMessage} />

{#await loadSrc(src)}
loading...
{:then data}
{#each cats as { name }, i}
<li>{name}</li>
{/each}

<!-- Keyed Each Block -->
{#each cats as cat (cat.id)}
<li>{cat.name}</li>
{/each}
{:catch err}
{@debug err}
{#await solveErr(err, {x: 'asdf'}) then reason}{@html reason}{/await}
{/await}

<style>p {font-family: 'Comic Sans MS', cursive;}</style>
91 changes: 91 additions & 0 deletions lib/rouge/lexers/svelte.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# -*- coding: utf-8 -*- #
# frozen_string_literal: true

module Rouge
module Lexers
load_lexer 'html.rb'

class Svelte < HTML
desc 'Svelte single-file components (https://svelte.dev/)'
tag 'svelte'
filenames '*.svelte'
mimetypes 'text/x-svelte', 'application/x-svelte'

def initialize(*)
super
# todo add support for typescript script blocks
@js = Javascript.new(options)
end

# Shorthand syntax for passing attributes - ex, `{src}` instead of `src={src}`
prepend :tag do
rule %r/(\{)\s*([a-zA-Z0-9_]+)\s*(})/m do
groups Str::Interpol, Name::Variable, Str::Interpol
pop!
end
end

prepend :attr do
# Duplicate template_start mixin here with a pop!
# Because otherwise we'll never exit the attr state
rule %r/\{/ do
token Str::Interpol
pop!
push :template
end
end

# handle templates within attribute single/double quotes
prepend :dq do
mixin :template_start
end

prepend :sq do
mixin :template_start
end

prepend :root do
# detect curly braces within HTML text (outside of tags/attributes)
rule %r/([^<&{]*)(\{)(\s*)/ do
groups Text, Str::Interpol, Text
push :template
end
end

state :template_start do
# open template
rule %r/\s*\{\s*/, Str::Interpol, :template
end

state :template do
# template end
rule %r/}/, Str::Interpol, :pop!

# Allow JS lexer to handle matched curly braces within template
rule(/(?<=^|[^\\])\{.*?(?<=^|[^\\])\}/) do
delegate @js
end

# keywords
rule %r/@(debug|html)\b/, Keyword
rule %r/(#await)(.*)(then|catch)(\s+)(\w+)/ do |m|
token Keyword, m[1]
delegate @js, m[2]
token Keyword, m[3]
token Text, m[4]
delegate @js, m[5]
end
rule %r/([#\/])(await|each|if|key)\b/, Keyword
rule %r/(:else)(\s+)(if)?\b/ do
groups Keyword, Text, Keyword
end
rule %r/:?(catch|then)\b/, Keyword

# allow JS parser to handle anything that's not a curly brace
rule %r/[^{}]+/ do
delegate @js
end
end
end
end
end
18 changes: 18 additions & 0 deletions spec/lexers/svelte_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

describe Rouge::Lexers::Svelte do
let(:subject) { Rouge::Lexers::Svelte.new }

describe 'guessing' do
include Support::Guessing

it 'guesses by filename' do
assert_guess :filename => 'foo.svelte'
end

it 'guesses by mimetype' do
assert_guess :mimetype => 'text/x-svelte'
assert_guess :mimetype => 'application/x-svelte'
end
end
end
78 changes: 78 additions & 0 deletions spec/visual/samples/svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<!-- This file is made up from several examples on https://svelte.dev/examples and is not expected to function. -->
<script>
let name = 'rick';
let cats = [
{ name: 'Keyboard Cat' },
{ name: 'Maru' },
{ name: 'Henri The Existential Cat' }
];

function doStuff() {
console.log('standard javascript is cool');
return false;
};
</script>

<script lang="ts">
import Nested from './Nested.svelte';

let src = '/tutorial/image.gif';
let count: number = 1;

// the `$:` is special in svelte
$: doubled = count * 2;

async function doMoreStuff(): boolean {
console.log('but typescript has more stuff!');
return false;
};
</script>

<h1 false tooltip="welcome" text=hi>Page Title</h1>

<img alt="{name} dancing" {src} />
<Nested user={name.toUpperCase(false, 42, {key: 'value'})} tooltip="I'm helping" false text=asdf on:message={handleMessage} />

<input bind:value={name} placeholder="enter your name" />
<h1>Hey {name}, check out our { cats.length } cats! Hello {name}!</h1>

Hey { name}, check out our { cats.length } cats!

{#if x > 10}
<p>{x} is greater than 10</p>
{:else if 5 > x}
<p>{x} is less than 5</p>
{:else}
<p>{x} is between 5 and 10</p>
{/if}

<ul>
{#each cats as { name }, i}
<li>{name}</li>
{/each}

<!-- Keyed Each Block -->
{#each cats as cat (cat.id)}
<li>{cat.name}</li>
{/each}
</ul>

{#await promise}
loading...
{:then data}
{@html data}
{:catch err}
{@debug err}
{/await}

{#await Fn('asdf', 42, {x: 'asdf'}) then data}
loading...
{/await}

<style>
p {
color: purple;
font-family: 'Comic Sans MS', cursive;
font-size: 2em;
}
</style>

0 comments on commit b584ea1

Please sign in to comment.