text:
/*===-- wtk/foundation/support.h ----------------------------*- mode: C -*-===*/
/*                                                                            */
/*                                     _   _                                  */
/*                               _ _ _| |_| |_                                */
/*                              | | | |  _| '_|                               */
/*                              |_____|_| |_,_|                               */
/*                                                                            */
/*       This file is distributed under the terms described in LICENSE.       */
/*                                                                            */
/*===----------------------------------------------------------------------===*/

#ifndef _WTK_FOUNDATION_SUPPORT_H_
#define _WTK_FOUNDATION_SUPPORT_H_

#include "wtk/config.h"
#include "wtk/linkage.h"

/**
 * \def WTK_INLINE
 * \brief Code should be inlined.
 */
#if defined(DOXYGEN)
  #define WTK_INLINE
#else
  #if defined(_MSC_VER)
    #define WTK_INLINE __forceinline
  #elif defined(__clang__) || defined(__GNUC__)
    #define WTK_INLINE __inline __attribute__ ((always_inline))
  #endif
#endif

/**
 * \def WTK_TRAP
 * \brief Errant, but reachable, code path.
 */
#if defined(DOXYGEN)
  #define WTK_TRAP()
#else
  #if defined(_MSC_VER)
    #define WTK_TRAP() __debugbreak()
  #elif defined(__GNUC__)
    #define WTK_TRAP() __builtin_trap()
  #endif
#endif

/**
 * \def WTK_UNREACHABLE
 * \brief Code is unreachable.
 */
#if defined(DOXYGEN)
  #define WTK_UNREACHABLE()
#else
  #if defined(_MSC_VER)
    #define WTK_UNREACHABLE() __assume(0)
  #elif defined(__clang__)
    #define WTK_UNREACHABLE() __builtin_unreachable()
  #elif defined(__GNUC__)
    #if __GNUC_VERSION__ >= 40500
      #define WTK_UNREACHABLE() __builtin_unreachable()
    #else
      #include <signal.h>
      #define WTK_UNREACHABLE() do { ::signal(SIGTRAP); } while(0, 0)
    #endif
  #endif
#endif

#endif /* _WTK_FOUNDATION_SUPPORT_H_ */


text:
/*===-- wtk/foundation/utilities.h --------------------------*- mode: C -*-===*/
/*                                                                            */
/*                                     _   _                                  */
/*                               _ _ _| |_| |_                                */
/*                              | | | |  _| '_|                               */
/*                              |_____|_| |_,_|                               */
/*                                                                            */
/*       This file is distributed under the terms described in LICENSE.       */
/*                                                                            */
/*===----------------------------------------------------------------------===*/

#ifndef _WTK_FOUNDATION_UTILITIES_H_
#define _WTK_FOUNDATION_UTILITIES_H_

#include "wtk/config.h"
#include "wtk/linkage.h"

#include "wtk/foundation/types.h"

WTK_BEGIN_EXTERN_C

/**
 * \def WTK_MIN
 * \brief Computes minimum of @a and @b.
 */
#define WTK_MIN(a, b) \
  (((a) > (b)) ? (b) : (a))

/**
 * \def WTK_MAX
 * \brief Computes maximum of @a and @b.
 */
#define WTK_MAX(a, b) \
  (((a) > (b)) ? (a) : (b))

/**
 * \def WTK_CLAMP
 * \brief Clamps @v between @min and @max, inclusive.
 */
#define WTK_CLAMP(v, min, max) \
  WTK_MIN(max, WTK_MAX(min, v))

/**
 * \def WTK_BITS_TO_BYTES
 * \brief Computes the number of bytes required to represent @n bits.
 */
#define WTK_BITS_TO_BYTES(n) \
  (((n) + 7) / 8)

/**
 * \def WTK_BYTES_TO_BITS
 * \brief Computes the number of bits represented by @n bytes.
 */
#define WTK_BYTES_TO_BITS(n) \
  ((n) * 8)

/**
 * \def WTK_ALIGN_TO_BOUNDARY
 * \brief Aligns @ptr to @alignment bytes boundary.
 */
#define WTK_ALIGN_TO_BOUNDARY(ptr, alignment) \
  ((((ptr) + (alignment) - 1) / (alignment)) * (alignment))

/**
 * \def WTK_IS_POWER_OF_TWO
 * \brief Quickly determines if @n is a power of two.
 */
#define WTK_IS_POWER_OF_TWO(n) \
  ((n) && !((n) & ((n) - 1)))

WTK_END_EXTERN_C

#endif /* _WTK_FOUNDATION_UTILITIES_H_ */

repo_name: origamicomet/wtk

repo_language: C

mime_type: text/x-c


text:
/*===-- wtk/foundation/memory.h -----------------------------*- mode: C -*-===*/
/*                                                                            */
/*                                     _   _                                  */
/*                               _ _ _| |_| |_                                */
/*                              | | | |  _| '_|                               */
/*                              |_____|_| |_,_|                               */
/*                                                                            */
/*       This file is distributed under the terms described in LICENSE.       */
/*                                                                            */
/*===----------------------------------------------------------------------===*/

#ifndef _WTK_FOUNDATION_MEMORY_H_
#define _WTK_FOUNDATION_MEMORY_H_

#include "wtk/config.h"
#include "wtk/linkage.h"

#include "wtk/foundation/types.h"

WTK_BEGIN_EXTERN_C

/*! Tries to allocate @size bytes aligned to @alignment boundary. */
extern WTK_PRIVATE
  void *wtk_allocate(
    wtk_size_t size,
    wtk_size_t alignment);

/*! Allocates @size bytes aligned to @alignment boundary. */
extern WTK_PRIVATE
 void *wtk_allocate_s(
  wtk_size_t size,
  wtk_size_t alignment);

/*! Frees memory allocated through `wtk_allocate` or `wtk_allocate_s`. */
extern WTK_PRIVATE
  void wtk_free(
    void *ptr);

/* TODO(mtwilliams): Bookmarking. Maintain a linked list of positions that can
 * be rewound to. If rewinding to bookmark prior to the last bookmark, remove
 * it from the list to let rewinds cascade. */

/* A simple bump allocator. */
typedef struct wtk_block {
  wtk_uintptr_t base;
  wtk_size_t    offset;
  wtk_size_t    size;
} wtk_block_t;

extern WTK_PRIVATE
  void *wtk_block_allocate(
    wtk_block_t *block,
    wtk_size_t size);

extern WTK_PRIVATE
  void *wtk_block_allocate_s(
    wtk_block_t *block,
    wtk_size_t size);

extern WTK_PRIVATE
  void wtk_block_deallocate(
    wtk_block_t *block,
    void *ptr);

extern WTK_PRIVATE
  void wtk_block_reset(
    wtk_block_t *block);

WTK_END_EXTERN_C

#endif /* _WTK_FOUNDATION_MEMORY_H_ */


text:
/*===-- wtk/foundation/types.h ------------------------------*- mode: C -*-===*/
/*                                                                            */
/*                                     _   _                                  */
/*                               _ _ _| |_| |_                                */
/*                              | | | |  _| '_|                               */
/*                              |_____|_| |_,_|                               */
/*                                                                            */
/*       This file is distributed under the terms described in LICENSE.       */
/*                                                                            */
/*===----------------------------------------------------------------------===*/

#ifndef _WTK_FOUNDATION_TYPES_H_
#define _WTK_FOUNDATION_TYPES_H_

#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>

WTK_BEGIN_EXTERN_C

/*
 * Standard
 */

/* TODO(mtwilliams): Fallbacks for older versions of Visual C/C++. */

typedef uintptr_t wtk_uintptr_t;
typedef size_t wtk_size_t;

typedef int8_t wtk_int8_t;
typedef uint8_t wtk_uint8_t;
typedef int16_t wtk_int16_t;
typedef uint16_t wtk_uint16_t;
typedef int32_t wtk_int32_t;
typedef uint32_t wtk_uint32_t;
typedef int64_t wtk_int64_t;
typedef uint64_t wtk_uint64_t;

typedef float wtk_float32_t;
typedef double wtk_float64_t;

/*
 * Custom
 */

typedef struct wtk_rectangle {
  wtk_uint32_t x, y, w, h;
} wtk_rectangle_t;

typedef struct wtk_color {
  wtk_uint32_t rgba;
} wtk_color_t;

WTK_END_EXTERN_C

#endif /* _WTK_FOUNDATION_TYPES_H_ */


text:
/*===-- wtk/foundation/assert.h -----------------------------*- mode: C -*-===*/
/*                                                                            */
/*                                     _   _                                  */
/*                               _ _ _| |_| |_                                */
/*                              | | | |  _| '_|                               */
/*                              |_____|_| |_,_|                               */
/*                                                                            */
/*       This file is distributed under the terms described in LICENSE.       */
/*                                                                            */
/*===----------------------------------------------------------------------===*/

#ifndef _WTK_ASSERT_H_
#define _WTK_ASSERT_H_

#include "wtk/config.h"
#include "wtk/linkage.h"

/* TODO(mtwilliams): Custom assertions. */
#include <assert.h>

WTK_BEGIN_EXTERN_C

#if WTK_CONFIGURATION <= WTK_CONFIGURATION_RELEASE
  #define wtk_assert_release(_Predicate) \
    assert(_Predicate)
#else
  #define wtk_assert_release(...)
#endif

#if WTK_CONFIGURATION <= WTK_CONFIGURATION_DEBUG
  #define wtk_assert_debug(_Predicate) \
    assert(_Predicate)
#else
  #define wtk_assert_debug(...)
#endif

#if WTK_PARANOID
  #define wtk_assert_paranoid(_Predicate) \
    assert(_Predicate)
#else
  #define wtk_assert_paranoid(...)
#endif

WTK_END_EXTERN_C

#endif /* _WTK_ASSERT_H_ */


text:
theme: jekyll-theme-hacker
markdown: CommonMarkGhPages
commonmark:
  extensions: ["table"]

repo_name: leafOfTree/vim-svelte-plugin

repo_language: Vim Script

mime_type: text/plain


text:
# vim-svelte-plugin

Vim syntax and indent plugin for `.svelte` files. Forked from [vim-vue-plugin][3].

## Installation

How to install

- [VundleVim][2]

        Plugin 'leafOfTree/vim-svelte-plugin'

- [vim-pathogen][5]

        cd ~/.vim/bundle && \
        git clone https://github.com/leafOfTree/vim-svelte-plugin --depth 1

- [vim-plug][7]

        Plug 'leafOfTree/vim-svelte-plugin'
        :PlugInstall

- Or manually, clone this plugin to `path/to/this_plugin`, and add it to `rtp` in vimrc

        set rtp+=path/to/this_plugin

This plugin works if it has set `filetype` to `svelte`. Please stay up to date. Feel free to open an issue or pull request.

## How it works

It combines HTML, CSS and JavaScript syntax and indent in one file.

Supports

- Svelte directives.
- Less/Sass/Scss, Pug with [vim-pug][4], Coffee with [vim-coffee-script][6], TypeScript with [typescript-vim][14] or [yats.vim][15].^

    Relative plugins need to be installed.

- A builtin `foldexpr` foldmethod.^
- [emmet-vim][10] HTML/CSS/JavaScript filetype detection.

^: see Configuration for details.

## Configuration

Set global variable to `1` to enable or `0` to disable. Ex:

    let g:vim_svelte_plugin_load_full_syntax = 1

| variable                               | description                                                                                | default |
|----------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| g:vim_svelte_plugin_load_full_syntax \* | Enable: load all syntax files in `runtimepath` to enable related syntax plugins.
 Disable: only in `$VIMRUNTIME/syntax`, `~/.vim/syntax` and `$VIM/vimfiles/syntax` | 0       |
| g:vim_svelte_plugin_use_pug \*          | Enable pug syntax for `<template lang="pug">`.                                                                                | 0       |
| g:vim_svelte_plugin_use_coffee         | Enable coffee syntax for `<script lang="coffee">`.                                                                                | 0       |
| g:vim_svelte_plugin_use_typescript     | Enable typescript syntax for `<script lang="ts">`.                                                                                | 0       |
| g:vim_svelte_plugin_use_less           | Enable less syntax for `<style lang="less">`.                                                                                | 0       |
| g:vim_svelte_plugin_use_sass           | Enable scss syntax for `<style lang="scss">`(or sass for `lang="sass"`).                                                                                | 0       |
| g:vim_svelte_plugin_has_init_indent    | Initially indent one tab inside `style/script` tags.                                                                                | 1       |
| g:vim_svelte_plugin_use_foldexpr       | Enable builtin `foldexpr` foldmethod.                                                                                | 0       |
| g:vim_svelte_plugin_debug              | Echo debug messages in `messages` list. Useful to debug if unexpected indents occur.                                                                                | 0       |

\*: Vim may be slow if the feature is enabled. Find a balance between syntax highlight and speed. By the way, custom syntax can be added in `~/.vim/syntax` or `$VIM/vimfiles/syntax`.

**Note**

- `filetype` is set to `svelte` so autocmds and other custom settings for `javascript` have to be manually enabled for `svelte`.
- `g:vim_svelte_plugin_load_full_syntax` applies to `HTML/Sass/Less`.
- See <https://svelte.dev/docs#svelte_preprocess> for how to use `Less/Sass/Pug`... in svelte.

### Neovim & [Treesiter](https://github.com/nvim-treesitter/nvim-treesitter)

For css syntax highlighting you will need the css language parser for [Treesiter](https://github.com/nvim-treesitter/nvim-treesitter). You can install it like so:

```
:TSInstall css
```

## Context based behavior

As there are more than one language in `.svelte` file, the different behaviors like mapping, completion, and local options may be expected according to tags or subtypes(current language type).

This plugin provides functions to get the tag/subtype where the cursor is in.

- **GetSvelteTag() => String**: Return value is one of `'template', 'script', 'style'`.

  ```vim
  " Example
  autocmd FileType svelte inoremap <buffer><expr> : InsertColon()

  function! InsertColon()
    let tag = GetSvelteTag()
    return tag == 'template' ? ':' : ': '
  endfunction
  ```

- **GetSvelteSubtype() => String**: Return value is one of `'html', 'javascript', 'css', 'scss', ...`.

- **OnChangeSvelteSubtype(subtype)**: An event listener that is called when subtype changes.

    You can also define an event listener function `OnChangeSvelteSubtype(subtype)` in your `vimrc` to get the subtype and set its local options whenever it changes.

    ```vim
    " Example: set local options based on subtype
    function! OnChangeSvelteSubtype(subtype)
      echom 'Subtype is '.a:subtype
      if empty(a:subtype) || a:subtype == 'html'
        setlocal commentstring=<!--%s-->
        setlocal comments=s:<!--,m:\ \ \ \ ,e:-->
      elseif a:subtype =~ 'css'
        setlocal comments=s1:/*,mb:*,ex:*/ commentstring&
      else
        setlocal commentstring=//%s
        setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
      endif
    endfunction
    ```

### emmet-vim

Currently emmet-vim works regarding your HTML/CSS/JavaScript emmet settings, but it depends on how emmet-vim gets `filetype` and may change in the future. Feel free to report an issue if any problem appears.

## Avoid overload

Since there are many sub languages included, most delays come from syntax files overload. A variable named `b:current_loading_main_syntax` is set to `svelte` which can be used as loading condition if you'd like to manually find and modify the syntax files causing overload.

For example, the builtin syntax `sass.vim` and `less.vim` in vim8.1 runtime always load `css.vim` which this plugin already loads. It can be optimized like

```diff
- runtime! syntax/css.vim
+ if !exists("b:current_loading_main_syntax")
+   runtime! syntax/css.vim
+ endif
```

## See also

- [vim-svelte-theme][11] svelte syntax color
- [vim-vue-plugin][3]
- [mxw/vim-jsx][1]

## License

This plugin is under [The Unlicense][8]. Other than this, `lib/indent/*` files are extracted from vim runtime.

[1]: https://github.com/mxw/vim-jsx "mxw: vim-jsx"
[2]: https://github.com/VundleVim/Vundle.vim
[3]: https://github.com/leafOfTree/vim-vue-plugin
[4]: https://github.com/digitaltoad/vim-pug
[5]: https://github.com/tpope/vim-pathogen
[6]: https://github.com/kchmck/vim-coffee-script
[7]: https://github.com/junegunn/vim-plug
[8]: https://choosealicense.com/licenses/unlicense/
[10]: https://github.com/mattn/emmet-vim
[11]: https://github.com/leafOfTree/vim-svelte-theme
[12]: https://travis-ci.com/leafOfTree/vim-svelte-plugin.svg?branch=master
[14]: https://github.com/leafgarland/typescript-vim
[15]: https://github.com/HerringtonDarkholme/yats.vim


text:
name: vim-svelte-plugin test
on: [push]
jobs:
  Basic-Test:
    runs-on: ubuntu-latest
    steps:
      - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
      - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
      - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
      - name: Check out repository code
        uses: actions/checkout@v3
      - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
      - run: echo "🖥 The workflow is now ready to test your code on the runner."
      - name: List files in the repository
        run: |
          ls ${{ github.workspace }}

      - name: Install neovim
        run: |
          sudo add-apt-repository ppa:neovim-ppa/stable -y
          sudo apt-get update
          sudo apt-get -y install neovim
      - name: Clone vim-test and syntax dependencies
        run: |
          git clone https://github.com/leafOfTree/vim-test ../vim-test --depth=1
          git clone https://github.com/digitaltoad/vim-pug ../vim-pug --depth=1
          git clone https://github.com/kchmck/vim-coffee-script ../vim-coffee-script --depth=1
          git clone https://github.com/leafgarland/typescript-vim ../typescript-vim --depth=1

      - name: Run test
        run: |
          bash ../vim-test/start.sh svelte vim-svelte-plugin

      - run: echo "🍏 This job's status is ${{ job.status }}."


text:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Vim syntax file
"
" Language: Svelte
" Maintainer: leaf <leafvocation@gmail.com>
"
" CREDITS: Inspired by mxw/vim-jsx.
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if exists("b:current_syntax") && b:current_syntax == 'svelte'
  finish
endif

" For advanced users, this variable can be used to avoid overload
let b:current_loading_main_syntax = 'svelte'
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Config {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:load_full_syntax = svelte#GetConfig('load_full_syntax', 0)
let s:use_pug = svelte#GetConfig('use_pug', 0)
let s:use_less = svelte#GetConfig('use_less', 0)
let s:use_sass = svelte#GetConfig('use_sass', 0)
let s:use_coffee = svelte#GetConfig('use_coffee', 0)
let s:use_typescript = svelte#GetConfig('use_typescript', 0)
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Functions {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! s:LoadSyntax(group, type)
  if s:load_full_syntax
    call s:LoadFullSyntax(a:group, a:type)
  else
    call s:LoadDefaultSyntax(a:group, a:type)
  endif
endfunction

function! s:LoadDefaultSyntax(group, type)
  unlet! b:current_syntax
  let syntaxPaths = ['$VIMRUNTIME', '$VIM/vimfiles', '$HOME/.vim']
  for path in syntaxPaths
    let file = expand(path).'/syntax/'.a:type.'.vim'
    if filereadable(file)
      execute 'syntax include '.a:group.' '.file
    endif
  endfor
endfunction

function! s:LoadFullSyntax(group, type)
  call s:SetCurrentSyntax(a:type)
  exec 'syntax include '.a:group.' syntax/'.a:type.'.vim'
endfunction

" Settings to avoid syntax overload
function! s:SetCurrentSyntax(type)
  if a:type == 'coffee'
    syntax cluster coffeeJS contains=@htmlJavaScript

    " Avoid overload of `javascript.vim`
    let b:current_syntax = 'svelte'
  else
    unlet! b:current_syntax
  endif
endfunction
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Load main syntax {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Load syntax/html.vim to syntax group, which loads full JavaScript and CSS
" syntax. It defines group htmlJavaScript and htmlCss.
call s:LoadSyntax('@HTMLSyntax', 'html')

" Load svelte-html syntax
syntax include syntax/svelte-html.vim

" Avoid overload
if !hlexists('cssTagName')
  call s:LoadSyntax('@htmlCss', 'css')
endif

" Avoid overload
if !hlexists('javaScriptComment')
  call s:LoadSyntax('@htmlJavaScript', 'javascript')
endif
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Load pre-processors syntax {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" If pug is enabled, load vim-pug syntax
if s:use_pug
  call s:LoadFullSyntax('@PugSyntax', 'pug')
endif

" If less is enabled, load less syntax
if s:use_less
  call s:LoadSyntax('@LessSyntax', 'less')
  runtime! after/syntax/less.vim
endif

" If sass is enabled, load sass syntax
if s:use_sass
  call s:LoadSyntax('@SassSyntax', 'sass')
  runtime! after/syntax/sass.vim
endif

" If CoffeeScript is enabled, load the syntax. Keep name consistent with
" vim-coffee-script/after/html.vim
if s:use_coffee
  call s:LoadFullSyntax('@htmlCoffeeScript', 'coffee')
endif

" If TypeScript is enabled, load the syntax.
if s:use_typescript
  call s:LoadFullSyntax('@TypeScript', 'typescript')
endif
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Syntax highlight {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" All start with html/javascript/css for emmet-vim in-file type detection
" Normal tag template
syntax region htmlSvelteTemplate fold
      \ start=+<[-:a-zA-Z0-9]\+[^>]*>$+
      \ end=+^</[-:a-zA-Z0-9]\+>+
      \ keepend contains=@HTMLSyntax
" Start tag across multiple lines or Empty tag across multiple lines
syntax region htmlSvelteTemplate fold
      \ start=+<[-:a-zA-Z0-9]\+[^>]*$+
      \ end=+^\(<\/[-:a-zA-Z0-9]\+>\|\(\S[^<]*\)\?\/>\)+
      \ keepend contains=@HTMLSyntax

" Tag in one line
syntax match htmlSvelteTemplate fold
      \ +<[-:a-zA-Z0-9]\+[^>]*>.*</[-:a-zA-Z0-9]\+>+
      \ contains=@HTMLSyntax
" Empty tag in one line
syntax match htmlSvelteTemplate fold
      \ +<[-:a-zA-Z0-9]\+[^>]*/>+
      \ contains=@HTMLSyntax
" @html,@debug tag in one line
syntax match htmlSvelteTemplate fold
      \ +{@\(html\|debug\)[^}]*}+
      \ contains=@HTMLSyntax
" Control blocks like {#if ...}, {#each ...}
syntax region htmlSvelteTemplate fold
      \ start=+{#[-a-zA-Z0-9]\+[^}]*}+
      \ end=+^{/[-a-zA-Z0-9]\+}+
      \ keepend contains=@HTMLSyntax

syntax region javascriptSvelteScript fold
      \ start=+<script[^>]*>+
      \ end=+</script>+
      \ keepend
      \ contains=@htmlJavaScript,jsImport,jsExport,svelteTag,svelteKeyword

syntax region cssSvelteStyle fold
      \ start=+<style[^>]*>+
      \ end=+</style>+
      \ keepend contains=@htmlCss,svelteTag

" Preprocessors syntax
syntax region pugSvelteTemplate fold
      \ start=+<template[^>]*lang="pug"[^>]*>+
      \ end=+</template>+
      \ keepend contains=@PugSyntax,svelteTag

syntax region coffeeSvelteScript fold
      \ start=+<script[^>]*lang="coffee"[^>]*>+
      \ end=+</script>+
      \ keepend contains=@htmlCoffeeScript,jsImport,jsExport,svelteTag

syntax region typescriptSvelteScript fold
      \ start=+<script[^>]*lang="\(ts\|typescript\)"[^>]*>+
      \ end=+</script>+
      \ keepend contains=@TypeScript,svelteTag

syntax region cssLessSvelteStyle fold
      \ start=+<style[^>]*lang="less"[^>]*>+
      \ end=+</style>+
      \ keepend contains=@LessSyntax,svelteTag
syntax region cssSassSvelteStyle fold
      \ start=+<style[^>]*lang="sass"[^>]*>+
      \ end=+</style>+
      \ keepend contains=@SassSyntax,svelteTag
syntax region cssScssSvelteStyle fold
      \ start=+<style[^>]*lang="scss"[^>]*>+
      \ end=+</style>+
      \ keepend contains=@SassSyntax,svelteTag

syntax region svelteTag
      \ start=+^<[^/]+ end=+>+  skip=+></+
      \ contained contains=htmlTagN,htmlString,htmlArg fold
syntax region svelteTag
      \ start=+^</+ end=+>+
      \ contained contains=htmlTagN,htmlString,htmlArg
syntax keyword svelteKeyword $ contained

highlight default link svelteTag htmlTag
highlight default link svelteKeyword Keyword
highlight default link cssUnitDecorators2 Number
highlight default link cssKeyFrameProp2 Constant
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Syntax patch {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Patch 7.4.1142
if has("patch-7.4-1142")
  if has("win32")
    syn iskeyword @,48-57,_,128-167,224-235,$
  else
    syn iskeyword @,48-57,_,192-255,$
  endif
endif

" Style
" Redefine (less|sass)Definition to highlight <style> correctly and
" enable emmet-vim css type.
if s:use_less
  silent! syntax clear lessDefinition
  syntax region cssLessDefinition matchgroup=cssBraces
        \ contains=@LessSyntax,cssLessDefinition
        \ contained containedin=cssLessSvelteStyle
        \ start="{" end="}"
endif
if s:use_sass
  silent! syntax clear sassDefinition
  syntax region cssSassDefinition matchgroup=cssBraces
        \ contains=@SassSyntax,cssSassDefinition
        \ contained containedin=cssScssSvelteStyle,cssSassSvelteStyle
        \ start="{" end="}"
endif

" Avoid css syntax interference
silent! syntax clear cssUnitDecorators
" Have to use a different name
syntax match cssUnitDecorators2
      \ /\(#\|-\|+\|%\|mm\|cm\|in\|pt\|pc\|em\|ex\|px\|ch\|rem\|vh\|vw\|vmin\|vmax\|dpi\|dppx\|dpcm\|Hz\|kHz\|s\|ms\|deg\|grad\|rad\)\ze\(;\|$\)/
      \ contained
      \ containedin=cssAttrRegion,sassCssAttribute,lessCssAttribute

silent! syntax clear cssKeyFrameProp
syn match cssKeyFrameProp2 /\d*%\|from\|to/
      \ contained nextgroup=cssDefinition
      \ containedin=cssAttrRegion,sassCssAttribute,lessCssAttribute

" HTML
" Clear htmlHead that may cause highlighting out of bounds
silent! syntax clear htmlHead

" JavaScript
" Number with minus
syntax match javaScriptNumber '\v<-?\d+L?>|0[xX][0-9a-fA-F]+>'
      \ containedin=@javascriptSvelteScript display

" TypeScript
" Fix template string `...` breaking syntax highlighting
syntax region  typescriptTemplate
  \ start=/`/  skip=/\\\\\|\\`\|\n/  end=/`\|$/
  \ contains=typescriptTemplateSubstitution,typescriptSpecial,@Spell
  \ containedin=typescriptObjectLiteral
  \ nextgroup=@typescriptSymbols
  \ skipwhite skipempty

" html5 data-*
syntax match htmlArg '\v<data(-[.a-z0-9]+)+>' containedin=@HTMLSyntax
"}}}

syntax sync fromstart
let b:current_syntax = 'svelte'
" vim: fdm=marker


text:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Config {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:highlight_svelte_attr = svelte#GetConfig('highlight_svelte_attr', 0)
")}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Syntax highlight {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
syntax match svelteComponentName containedin=htmlTagN '\v\C<[a-zA-Z0-9]+(\.[A-Z][a-zA-Z0-9]+)*>'

syntax match svelteComponentName containedin=htmlTagN '\v\C<[a-z0-9]+(-[a-z0-9]+)+>'

syntax match svelteComponentName containedin=htmlTagN '\vsvelte:\w*'

" Syntax for vim-svelte-theme
syntax match htmlAttr '\v(\S|\<)@<![^\/\<\>[:blank:]]+'
      \ containedin=htmlTag
      \ contains=htmlString,svelteValue,htmlArg
syntax match htmlAttrEqual '\v\=' containedin=htmlAttr

syntax match svelteAttr
      \ '\(\S\)\@<!\w\+:[^=>[:blank:]]\+\(="[^"]*"\|={[^}]*}\)\?'
      \ containedin=htmlTag
      \ contains=svelteKey,svelteValue
syntax match svelteValue contained '{[^}]*}'
syntax match svelteKey contained '\w\+:[^=>[:blank:]]\+'

syntax region svelteExpression
      \ containedin=htmlH.*,htmlItalic
      \ matchgroup=svelteBrace
      \ start="{"
      \ end="}\(}\|;\)\@!"

" Multiple lines expressions are supposed to end with '}}' or ')}'
syntax region svelteExpression
      \ containedin=svelteValue,htmlValue,htmlAttr
      \ contains=@simpleJavascriptExpression
      \ matchgroup=svelteBrace
      \ start="{"
      \ end="\(}\)\@<=}\|\()\)\@<=}"

syntax region svelteExpression
      \ containedin=htmlSvelteTemplate,svelteValue,htmlString,htmlArg,htmlTag,htmlAttr,htmlValue,htmlAttr
      \ contains=@simpleJavascriptExpression,svelteAtTags
      \ matchgroup=svelteBrace
      \ start="{"
      \ end="}\(}\|;\)\@!"
      \ oneline

syntax match svelteAtTags '@\(html\|debug\)'

syntax region svelteBlockBody
      \ containedin=htmlSvelteTemplate,htmlLink
      \ contains=@simpleJavascriptExpression,svelteBlockKeyword
      \ matchgroup=svelteBrace
      \ start="{:"
      \ end="}"

syntax region svelteBlockStart
      \ containedin=htmlSvelteTemplate,htmlLink
      \ contains=@simpleJavascriptExpression,svelteBlockKeyword
      \ matchgroup=svelteBrace
      \ start="{#"
      \ end="}"

syntax region svelteBlockEnd
      \ containedin=htmlSvelteTemplate,htmlLink
      \ contains=@simpleJavascriptExpression,svelteBlockKeyword
      \ matchgroup=svelteBrace
      \ start="{\/"
      \ end="}"

syntax keyword svelteBlockKeyword if else each await then catch as

syntax cluster simpleJavascriptExpression contains=\CjavaScript.*

" Redefine JavaScript syntax
syntax region javaScriptStringS
      \ start=+'+ skip=+\\\\\|\\'+ end=+'\|$+	contained
syntax region javaScriptStringD
      \ start=+"+ skip=+\\\\\|\\"+ end=+"\|$+	contained
syntax region javaScriptTemplateString
      \ start=+`+ skip=+\\`+ end=+`+ contained
      \ contains=javaScriptTemplateExpression
syntax region javaScriptTemplateExpression
      \ matchgroup=Type
      \ start=+${+ end=+}+ keepend contained

highlight default link svelteAttr htmlTag
if s:highlight_svelte_attr
  highlight default link svelteKey Type
  highlight default link svelteValue None
else
  highlight default link svelteKey htmlArg
  highlight default link svelteValue String
endif

highlight default link svelteExpression None
highlight default link svelteBrace Type
highlight default link svelteAtTags Type
highlight default link svelteBlockKeyword Statement
highlight default link svelteComponentName htmlTagName
highlight default link javaScriptTemplateString String
highlight default link javaScriptStringS String
highlight default link javaScriptStringD String
highlight default link javaScriptNumber	Constant
highlight default link javaScriptOperator	Operator
highlight default link svelteAttr	htmlTag
highlight default link svelteAttrEqual htmlTag
"}}}
" vim: fdm=marker


text:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Vim indent file
"
" Language: Svelte
" Maintainer: leafOfTree <leafvocation@gmail.com>
"
" CREDITS: Inspired by mxw/vim-jsx.
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
if exists("b:did_indent")
  finish
endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Variables {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:name = 'vim-svelte-plugin'
" Let <template> handled by HTML
let s:svelte_tag_start = '\v^\<(script|style)'
let s:svelte_tag_end = '\v^\<\/(script|style)'
let s:template_tag = '\v^\s*\<\/?template'
" https://developer.mozilla.org/en-US/docs/Glossary/Empty_element
let s:empty_tagname = '(area|base|br|col|embed|hr|input|img|keygen|link|meta|param|source|track|wbr)'
let s:empty_tag = '\v\C\<'.s:empty_tagname.'[^/]*\>'
let s:empty_tag_start = '\v\<'.s:empty_tagname.'[^\>]*$'
let s:empty_tag_end = '\v^\s*[^\<\>\/]*\>\s*'
let s:tag_end = '\v^\s*\/?\>\s*'
let s:oneline_block = '^\s*{#.*{/.*}\s*$'
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Config {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:use_pug = svelte#GetConfig('use_pug', 0)
let s:use_sass = svelte#GetConfig('use_sass', 0)
let s:use_coffee = svelte#GetConfig('use_coffee', 0)
let s:use_typescript = svelte#GetConfig('use_typescript', 0)
let s:has_init_indent = svelte#GetConfig('has_init_indent', 1)
let s:debug = svelte#GetConfig('debug', 0)
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Load indent method {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Save shiftwidth
let s:sw = &sw

" Use specific indent files for compatibility
unlet! b:did_indent
runtime indent/svelte-xml.vim

unlet! b:did_indent
runtime indent/svelte-css.vim

" Use normal indent files
unlet! b:did_indent
runtime! indent/javascript.vim
let b:javascript_indentexpr = &indentexpr

if s:use_pug
  unlet! b:did_indent
  let s:save_formatoptions = &formatoptions
  runtime! indent/pug.vim
  let &formatoptions = s:save_formatoptions
endif

if s:use_sass
  unlet! b:did_indent
  runtime! indent/sass.vim
endif

if s:use_coffee
  unlet! b:did_indent
  runtime! indent/coffee.vim
endif

if s:use_typescript
  unlet! b:did_indent
  runtime! indent/typescript.vim
endif

" Recover shiftwidth
let &sw = s:sw
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Settings {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" JavaScript indentkeys
setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e,:,=:else
" XML indentkeys
setlocal indentkeys+=*<Return>,<>>,<<>,/
setlocal indentexpr=GetSvelteIndent()
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Functions {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
function! GetSvelteIndent()
  let prevlnum = prevnonblank(v:lnum-1)
  let prevline = getline(prevlnum)
  let prevsyns = s:SynsSOL(prevlnum)

  let curline = getline(v:lnum)
  let cursyns = s:SynsSOL(v:lnum)
  let cursyn = get(cursyns, 0, '')

  if s:IsMultipleLineTemplateString(curline, cursyns)
    call s:Log('current line is multiline template string expression')
    if !s:IsMultipleLineTemplateString(prevline, prevsyns)
      let ind = indent(v:lnum - 1) + &sw
    else
      let ind = indent(v:lnum - 1)
    endif
  elseif s:SynHTML(cursyn) && !s:IsMultipleLineSvelteExpression(curline, cursyns)
    call s:Log('syntax: html')
    let ind = XmlIndentGet(v:lnum, 0)
    if prevline =~? s:empty_tag
      call s:Log('previous line is empty tag')
      let ind = ind - &sw
    endif

    if s:IsBlockStart(prevsyns) && prevline !~ s:oneline_block
      call s:Log('increase block indent')
      let ind = ind + &sw
    endif

    if s:IsBlockEnd(cursyns, curline)
      call s:Log('decrease block indent')
      let ind = ind - &sw
    endif

    " Align '/>' and '>' with '<' for multiline tags.
    if curline =~? s:tag_end
      let ind = ind - &sw
    endif
    " Then correct the indentation of any element following '/>' or '>'.
    if prevline =~? s:tag_end
      let ind = ind + &sw

      "Decrease indent if prevlines are a multiline empty tag
      let [start, end] = s:PrevMultilineEmptyTag(v:lnum)
      if end == prevlnum
        call s:Log('previous line is a multiline empty tag')
        let ind = indent(v:lnum - 1)
      endif
    endif
  elseif s:SynPug(cursyn)
    call s:Log('syntax: pug')
    let ind = GetPugIndent()
  elseif s:SynCoffee(cursyn)
    call s:Log('syntax: coffee')
    let ind = GetCoffeeIndent(v:lnum)
  elseif s:SynTypeScript(cursyn)
    call s:Log('syntax: typescript')
    let ind = GetTypescriptIndent()
  elseif s:SynSASS(cursyn)
    call s:Log('syntax: sass')
    let ind = GetSassIndent()
  elseif s:SynStyle(cursyn)
    call s:Log('syntax: style')
    let ind = GetCSSIndent()
  else
    call s:Log('syntax: javascript')
    if len(b:javascript_indentexpr)
      let ind = eval(b:javascript_indentexpr)
    else
      let ind = cindent(v:lnum)
    endif
  endif

  if curline =~? s:svelte_tag_start || curline =~? s:svelte_tag_end
        \|| prevline =~? s:svelte_tag_end
        \|| (curline =~ s:template_tag && s:SynPug(cursyn))
    call s:Log('current line is svelte tag or previous line is svelte tag end')
    call s:Log('... or current line is pug template tag')
    let ind = 0
  elseif s:has_init_indent
    if s:SynSvelteScriptOrStyle(cursyn) && ind == 0
      call s:Log('add initial indent')
      let ind = &sw
    endif
  elseif prevline =~? s:svelte_tag_start
    call s:Log('previous line is svelte tag start')
    let ind = 0
  endif

  call s:Log('indent: '.ind)
  return ind
endfunction

function! s:IsBlockStart(prevsyns)
  let prevsyn_second = get(a:prevsyns, 1, '')
  " Some HTML tags add an extra syntax layer
  let prevsyn_third = get(a:prevsyns, 2, '')
  return s:SynBlockBody(prevsyn_second)
        \ || s:SynBlockStart(prevsyn_second)
        \ || s:SynBlockBody(prevsyn_third)
        \ || s:SynBlockStart(prevsyn_third)
endfunction

function! s:IsBlockEnd(cursyns, curline)
  let cursyn_second = get(a:cursyns, 1, '')
  " Some HTML tags add an extra syntax layer
  let cursyn_third = get(a:cursyns, 2, '')
  return a:curline !~ '^\s*$'
        \ && (s:SynBlockBody(cursyn_second)
        \ || s:SynBlockEnd(cursyn_second)
        \ || s:SynBlockBody(cursyn_third)
        \ || s:SynBlockEnd(cursyn_third))
endfunction

function! s:SynsEOL(lnum)
  let lnum = prevnonblank(a:lnum)
  let col = strlen(getline(lnum))
  return map(synstack(lnum, col), 'synIDattr(v:val, "name")')
endfunction

function! s:SynsSOL(lnum)
  let lnum = prevnonblank(a:lnum)
  let col = match(getline(lnum), '\S') + 1
  return map(synstack(lnum, col), 'synIDattr(v:val, "name")')
endfunction

function! s:SynHTML(syn)
  return a:syn ==? 'htmlSvelteTemplate'
endfunction

function! s:IsMultipleLineSvelteExpression(curline, syns)
  if a:curline =~ '^\s*{.*}\s*$'
    return 0
  endif

  for syn in a:syns
    if syn ==? 'svelteExpression'
      return 1
    endif
  endfor

  return 0
endfunction

function! s:IsMultipleLineTemplateString(curline, syns)
  if a:curline =~ '^\s*{.*}\s*$'
    return 0
  endif

  for syn in a:syns
    if syn ==? 'javaScriptTemplateString'
      return 1
    endif
  endfor

  return 0
endfunction

function! s:SynBlockBody(syn)
  return a:syn ==? 'svelteBlockBody'
endfunction

function! s:SynBlockStart(syn)
  return a:syn ==? 'svelteBlockStart'
endfunction

function! s:SynBlockEnd(syn)
  return a:syn ==? 'svelteBlockEnd'
endfunction

function! s:SynPug(syn)
  return a:syn ==? 'pugSvelteTemplate'
endfunction

function! s:SynCoffee(syn)
  return a:syn ==? 'coffeeSvelteScript'
endfunction

function! s:SynTypeScript(syn)
  return a:syn ==? 'typescriptSvelteScript'
endfunction

function! s:SynSASS(syn)
  return a:syn ==? 'cssSassSvelteStyle'
endfunction

function! s:SynStyle(syn)
  return a:syn =~? 'SvelteStyle'
endfunction

function! s:SynSvelteScriptOrStyle(syn)
  return a:syn =~? '\v(SvelteStyle)|(SvelteScript)'
endfunction

function! s:PrevMultilineEmptyTag(lnum)
  let lnum = a:lnum - 1
  let lnums = [0, 0]
  while lnum > 0
    let line = getline(lnum)
    if line =~? s:empty_tag_end
      let lnums[1] = lnum
    endif

    if line =~? s:tag_start
      if line =~? s:empty_tag_start
        let lnums[0] = lnum
        return lnums
      else
        return [0, 0]
      endif
    endif

    let lnum = lnum - 1
  endwhile
endfunction

function! s:Log(msg)
  if s:debug
    echom '['.s:name.']['.v:lnum.'] '.a:msg
  endif
endfunction
"}}}

let b:did_indent = 1
" vim: fdm=marker


text:
" Language:	xml
" Maintainer:	Johannes Zellner <johannes@zellner.org>
" Last Change:	2017 Jun 13
" Notes:	1) does not indent pure non-xml code (e.g. embedded scripts)
"		2) will be confused by unbalanced tags in comments
"		or CDATA sections.
"		2009-05-26 patch by Nikolai Weibull
" TODO: 	implement pre-like tags, see xml_indent_open / xml_indent_close

" Only load this indent file when no other was loaded.
if exists("b:did_indent")
    finish
endif
let b:did_indent = 1
let s:keepcpo= &cpo
set cpo&vim

" [-- local settings (must come before aborting the script) --]
setlocal indentexpr=XmlIndentGet(v:lnum,1)
setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,}

if !exists('b:xml_indent_open')
    let b:xml_indent_open = '.\{-}<\a'
    " pre tag, e.g. <address>
    " let b:xml_indent_open = '.\{-}<[/]\@!\(address\)\@!'
endif

if !exists('b:xml_indent_close')
    let b:xml_indent_close = '.\{-}</'
    " end pre tag, e.g. </address>
    " let b:xml_indent_close = '.\{-}</\(address\)\@!'
endif

let &cpo = s:keepcpo
unlet s:keepcpo

" [-- finish, if the function already exists --]
if exists('*XmlIndentGet')
  finish
endif

let s:keepcpo= &cpo
set cpo&vim

fun! <SID>XmlIndentWithPattern(line, pat)
    let s = substitute('x'.a:line, a:pat, "\1", 'g')
    return strlen(substitute(s, "[^\1].*$", '', ''))
endfun

" [-- check if it's xml --]
fun! <SID>XmlIndentSynCheck(lnum)
    if '' != &syntax
	let syn1 = synIDattr(synID(a:lnum, 1, 1), 'name')
	let syn2 = synIDattr(synID(a:lnum, strlen(getline(a:lnum)) - 1, 1), 'name')
	if '' != syn1 && syn1 !~ 'xml' && '' != syn2 && syn2 !~ 'xml'
	    " don't indent pure non-xml code
	    return 0
	elseif syn1 =~ '^xmlComment' && syn2 =~ '^xmlComment'
	    " indent comments specially
	    return -1
	endif
    endif
    return 1
endfun

" [-- return the sum of indents of a:lnum --]
fun! <SID>XmlIndentSum(lnum, style, add)
    let line = getline(a:lnum)
    if a:style == match(line, '^\s*</')
	return (shiftwidth() *
	\  (<SID>XmlIndentWithPattern(line, b:xml_indent_open)
	\ - <SID>XmlIndentWithPattern(line, b:xml_indent_close)
	\ - <SID>XmlIndentWithPattern(line, '.\{-}/>'))) + a:add
    else
	return a:add
    endif
endfun

fun! XmlIndentGet(lnum, use_syntax_check)
    " Find a non-empty line above the current line.
    let lnum = prevnonblank(a:lnum - 1)

    " Hit the start of the file, use zero indent.
    if lnum == 0
	return 0
    endif

    if a:use_syntax_check
	let check_lnum = <SID>XmlIndentSynCheck(lnum)
	let check_alnum = <SID>XmlIndentSynCheck(a:lnum)
	if 0 == check_lnum || 0 == check_alnum
	    return indent(a:lnum)
	elseif -1 == check_lnum || -1 == check_alnum
	    return -1
	endif
    endif

    let ind = <SID>XmlIndentSum(lnum, -1, indent(lnum))
    let ind = <SID>XmlIndentSum(a:lnum, 0, ind)

    return ind
endfun

let &cpo = s:keepcpo
unlet s:keepcpo

" vim:ts=8


text:
" Vim indent file
" Language:	    CSS
" Maintainer:	    Nikolai Weibull <now@bitwi.se>
" Latest Revision:  2012-05-30
"		    Use of shiftwidth() added by Oleg Zubchenko.

" Changes: 1) Reacquire the line while checking comment lines.

if exists("b:did_indent")
  finish
endif
let b:did_indent = 1

setlocal indentexpr=GetCSSIndent()
setlocal indentkeys=0{,0},!^F,o,O
setlocal nosmartindent

let b:undo_indent = "setl smartindent< indentkeys< indentexpr<"

if exists("*GetCSSIndent")
  finish
endif
let s:keepcpo= &cpo
set cpo&vim

function s:prevnonblanknoncomment(lnum)
  let lnum = a:lnum
  while lnum > 1
    let lnum = prevnonblank(lnum)
    let line = getline(lnum)
    if line =~ '\*/'
      while lnum > 1 && line !~ '/\*'
        let lnum -= 1
        let line = getline(lnum)
      endwhile
      if line =~ '^\s*/\*'
        let lnum -= 1
      else
        break
      endif
    else
      break
    endif
  endwhile
  return lnum
endfunction

function s:count_braces(lnum, count_open)
  let n_open = 0
  let n_close = 0
  let line = getline(a:lnum)
  let pattern = '[{}]'
  let i = match(line, pattern)
  while i != -1
    if synIDattr(synID(a:lnum, i + 1, 0), 'name') !~ 'css\%(Comment\|StringQ\{1,2}\)'
      if line[i] == '{'
        let n_open += 1
      elseif line[i] == '}'
        if n_open > 0
          let n_open -= 1
        else
          let n_close += 1
        endif
      endif
    endif
    let i = match(line, pattern, i + 1)
  endwhile
  return a:count_open ? n_open : n_close
endfunction

function GetCSSIndent()
  let line = getline(v:lnum)
  if line =~ '^\s*\*'
    return cindent(v:lnum)
  endif

  let pnum = s:prevnonblanknoncomment(v:lnum - 1)
  if pnum == 0
    return 0
  endif

  return indent(pnum) + s:count_braces(pnum, 1) * shiftwidth()
        \ - s:count_braces(v:lnum, 0) * shiftwidth()
endfunction

let &cpo = s:keepcpo
unlet s:keepcpo


text:
if exists("b:did_ftplugin") | finish | endif
let b:did_ftplugin = 1

if !has('nvim')
  setlocal matchpairs+=<:>
endif

if exists("loaded_matchit")
  let b:match_ignorecase = 1
  let b:match_words = '<:>,' .
        \ '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' .
        \ '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' .
        \ '<\@<=\([^/][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>,' .
        \ '{#\(if\|each\)[^}]*}:{\:else[^}]*}:{\/\(if\|each\)},' .
        \ '{#await[^}]*}:{\:then[^}]*}:{\/await},'
endif

" Indent correctly with template string for vim-javascript/builtin
" indentexpr
let b:syng_str = '^\%(.*template\)\@!.*string\|special'
let b:syng_strcom = '^\%(.*template\)\@!.*string\|comment\|regex\|special\|doc'


text:
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Config {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:use_foldexpr = exists("g:vim_svelte_plugin_use_foldexpr")
      \ && g:vim_svelte_plugin_use_foldexpr == 1
"}}}

if !s:use_foldexpr | finish | endif

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Settings {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
setlocal foldmethod=expr
setlocal foldexpr=GetSvelteFold(v:lnum)
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Variables {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
let s:empty_line = '\v^\s*$'
let s:block_end = '\v^\s*}|]|\)'
let s:svelte_tag_start = '\v^\<\w+'
let s:svelte_tag_end = '\v^\<\/\w+'
let s:svelte_internal_blocks = '\v:(else|then|catch)'
"}}}

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"
" Functions {{{
"
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
"  see :h fold-expr
"  value			    meaning
"  0			        the line is not in a fold
"  1, 2, ..		    the line is in a fold with this level
"  -1			        the fold level is undefined, use the fold level of a
"			            line before or after this line, whichever is the
"			            lowest.
"  "="			      use fold level from the previous line
"  "a1", "a2", ..	add one, two, .. to the fold level of the previous
"			            line, use the result for the current line
"  "s1", "s2", ..	subtract one, two, .. from the fold level of the
"                 previous line, use the result for the next line
"  ">1", ">2", ..	a fold with this level starts at this line
"  "<1", "<2", ..	a fold with this level ends at this line
function! GetSvelteFold(lnum)
  let this_line = getline(a:lnum)
  let next_line = getline(a:lnum + 1)

  " Handle empty lines
  if this_line =~ s:empty_line
    return -1
  endif

  " Handle start/end tags
  if this_line =~ s:svelte_tag_start
    return '>1'
  endif
  if this_line =~ s:svelte_tag_end
    " If return '<1', fold will get incorrect with prev line
    return 1
  endif

  " Fold by indent
  if a:lnum > 1
    let prev_indent = s:IndentLevel(a:lnum - 1)
  else
    let prev_indent = 0
  endif
  let this_indent = s:IndentLevel(a:lnum)
  let next_indent = s:IndentLevel(s:NextNonBlankLine(a:lnum))

  " Fold separately on blocks
  if this_line =~ s:svelte_internal_blocks
    return '>'.next_indent
  endif

  if GetSvelteTag(a:lnum) == 'script'
    " Handle closing '}'
    if this_line =~ '\v^\s*},?\s*$'
      return '<'.prev_indent
    endif

    " --this
    " ----next
    if this_indent < next_indent
      return '>'.next_indent
    endif

    " ----this
    " --next
    if this_indent >= next_indent
      return this_indent
    endif
  else
    " Template or style
    return this_indent
  endif
endfunction

function! s:IndentLevel(lnum)
  " Add 1 to indentLevel, so start/end tags can fold properly
  return indent(a:lnum) / &shiftwidth + 1
endfunction

function! s:NextNonBlankLine(lnum)
  let next_line = a:lnum + 1
  let last_line = line('$')

  while next_line <= last_line
    if getline(next_line) =~ '\v\S'
      return next_line
    endif

    let next_line += 1
  endwhile

  return 0
endfunction
"}}}
" vim: fdm=marker


text:
let s:name = 'vim-svelte-plugin'
let s:debug = exists("g:vim_svelte_plugin_debug")
      \ && g:vim_svelte_plugin_debug == 1

function! svelte#GetConfig(name, default)
  let name = 'g:vim_svelte_plugin_'.a:name
  return exists(name) ? eval(name) : a:default
endfunction

" Since svelte#Log and svelte#GetConfig are always called
" in syntax and indent files,
" this file will be sourced when opening the first svelte file
if exists('##CursorMoved') && exists('*OnChangeSvelteSubtype')
  augroup vim_svelte_plugin
    autocmd!
    autocmd CursorMoved,CursorMovedI,WinEnter *.svelte
          \ call s:CheckSubtype()
  augroup END

  let s:subtype = ''
  function! s:CheckSubtype()
    let subtype = GetSvelteSubtype()

    if s:subtype != subtype
      call OnChangeSvelteSubtype(subtype)
      let s:subtype = subtype
    endif
  endfunction
endif

function! s:SynsEOL(lnum)
  let lnum = prevnonblank(a:lnum)
  let cnum = strlen(getline(lnum))
  return map(synstack(lnum, cnum), 'synIDattr(v:val, "name")')
endfunction

function! GetSvelteSubtype()
  let lnum = line('.')
  let cursyns = s:SynsEOL(lnum)
  let syn = !empty(cursyns) ? get(cursyns, 0, '') : ''

  let subtype = matchstr(syn, '\w\+\zeSvelte')
  if subtype =~ 'css\w\+'
    " For cssScss, cssLess, ...
    let subtype = subtype[3:]
  endif
  let subtype = tolower(subtype)
  return subtype
endfunction

function! GetSvelteTag(...)
  let lnum = a:0 > 0 ? a:1 : line('.')
  let cursyns = s:SynsEOL(lnum)
  let syn = get(cursyns, 0, '')

  if syn =~ 'SvelteTemplate'
    let tag = 'template'
  elseif syn =~ 'SvelteScript'
    let tag = 'script'
  elseif syn =~ 'SvelteStyle'
    let tag = 'style'
  else
    let tag = ''
  endif

  return tag
endfunction


text: autocmd BufNewFile,BufRead *.svelte setf svelte


text:
@ECHO OFF
pushd %~dp0
ECHO "%~1"
IF "%~1"=="" GOTO BLANK
mpv.exe --script=360plugin.lua --script-opts=360plugin-enabled=yes "%~1"
GOTO DONE
:BLANK
mpv.exe --script=360plugin.lua --script-opts=360plugin-enabled=yes
:DONE
popd

repo_name: dfaker/VR-reversal

repo_language: Lua


text:
-- If the file "../script-opts/360plugin.conf" (or name of this script) is present,
-- and enabled=yes is written to it,
-- or the command line argument "--script-opts=360plugin-enabled=yes" is passed,
-- the features of this script will be running without having to use the toggle key.
-- The following default key bindings can also be reconfigured in the same way
-- (including via CLI) without editing this script at all.
local opts = {
	["enabled"]=false,
	["toggle_vr360"]="v",
	["cycle_input"]="1",
	["cycle_output"]="2",
	["roll_left"]="u",
	["roll_right"]="o",
	["write_log"]="w",
	["pitch_up"]="i",
	["pitch_down"]="k",
	["yaw_up"]="l",
	["yaw_down"]="j",
	["easy_crop"]="c",
	["res_up"]="y",
	["res_down"]="h",
	["zoom_in"]="=",
	["zoom_out"]="-",
	["wzoom_out"]="WHEEL_DOWN",
	["wzoom_in"]="WHEEL_UP",
	["reset_view"]="0",
	["switch_stereo"]="r",
	["switch_eye"]="t",
	["switch_scaler"]="e",
	["switch_output_sbs"]="p",
	["toggle_smooth"]="g",
	["switch_bounds"]="b",
	["new_log_session"]="n",
	["grab_mouse"]="mouse_btn0",
	["mouse_pan"]="mouse_move",
	["show_help"]="?",
	["osc"]="no",
	["fullscreen"]="yes",
	["osd-font-size"]=30
}
(require 'mp.options').read_options(opts)

local saved_props = {
	["hwdec"] 			= "NIL",
	["fullscreen"] 		= "NIL",
	["osc"] 			= "NIL",
	["osd-font-size"] 	= "NIL",
	["cursor-autohide"] = "NIL"
}

local yaw   = 0.0
local last_yaw = 0.0
local init_yaw = 0.0

local pitch = 0.0
local last_pitch = 0.0
local init_pitch = 0.0

local roll  = 0.0
local last_roll  = 0.0
local init_roll = 0.0

local inputProjections = {
	"hequirect",
	"equirect",
	"fisheye",
	"pannini",
	"cylindrical",
	"sg"
}
local inputProjectionInd = 0
local inputProjection    = "hequirect"

local outputProjections = {
	"flat",
	"sg"
}

local outputProjectionInd = 0
local outputProjection    = "flat"

local idfov=180.0
local dfov=110.0
local last_dfov  = 110.0
local init_dfov = 0.0

local doit = 0.0
local res  = 1.0
local dragging = false

local smoothMouse = true

local scaling   = 'linear'

local in_stereo  = 'sbs'
local outputMode = '2d'
local out_stereo = '2d'
local anaglyph_filter = ""
local sarOutput = 1.0

local h_flip    = '0'
local in_flip   = ''

local interp    = 'cubic'

local startTime = nil

local filterIsOn = false

local mousePos = {}
local lasttimePos = nil
local filename = nil

local fileobjectNumber = 0
local fileobjectFilename = ''
local videofilename = ''
local file_object      = nil

local ffmpegCommandList = {}

local openNewLogFile = function()
	if lasttimePos ~= nil then
		fileobjectNumber = fileobjectNumber+1
	end
	videofilename = mp.get_property('filename')
	fileobjectFilename = string.format('%s_3dViewHistory_%s.txt',videofilename,fileobjectNumber)
	file_object = io.open(fileobjectFilename, 'w')
	lasttimePos = nil
end

local SecondsToClock = function(seconds)
	local seconds = tonumber(seconds)
	if seconds <= 0 then
		return "00:00:00";
	else
	hours = string.format("%02.f", math.floor(seconds/3600));
	mins = string.format("%02.f", math.floor(seconds/60 - (hours*60)));
	secs = string.format("%02.2f", seconds - hours*3600 - mins *60);
		return hours .. ":" .. mins .. ":" .. secs
	end
end

local writeHeadPositionChange = function()
	if filename == nil then
		filename = mp.get_property("path")
	end

	if file_object == nil then
		return
	else
		local initPass=false
		if lasttimePos == nil then
			lasttimePos = mp.get_property("time-pos")
			startTime   = lasttimePos
			initPass=true
			if lasttimePos == nil then
				return
			end
		end

		local newTimePos = mp.get_property("time-pos")

		if newTimePos == nil then
			return
		end

		local outputTs = string.format("%.3f-%.3f ",lasttimePos,newTimePos)
		local changedValues = {}
		local movementDuration = (newTimePos-lasttimePos)
		local maximumTimeoutReached = movementDuration > 5.0

		if initPass or pitch ~= last_pitch or maximumTimeoutReached then
			changedValues[#changedValues+1]= string.format(", [expr] v360 pitch 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_pitch,pitch,lasttimePos,movementDuration)
		end
		last_pitch=pitch

		if initPass or yaw ~= last_yaw or maximumTimeoutReached then
			changedValues[#changedValues+1]= string.format(", [expr] v360 yaw 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_yaw,yaw,lasttimePos,movementDuration)
		end
		last_yaw=yaw

		if initPass or roll ~= last_roll or maximumTimeoutReached then
			changedValues[#changedValues+1]= string.format(", [expr] v360 roll 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_roll,roll,lasttimePos,movementDuration)
		end
		last_roll=roll

		if initPass or dfov ~= last_dfov or maximumTimeoutReached then
			changedValues[#changedValues+1]= string.format(", [expr] v360 d_fov 'lerp(%.3f,%.3f,(T-%.3f)/%.3f)'",last_dfov,dfov,lasttimePos,movementDuration)
		end
		last_dfov=dfov

		if initPass then
			init_pitch = pitch
			init_yaw   = yaw
			init_roll  = roll
			init_dfov  = dfov
		end

		if #changedValues > 0 then
			local commandString = ''
			for k,changedValue in pairs(changedValues) do
				commandString = commandString .. changedValue
			end

			commandString = commandString:sub(2)

			commandString = outputTs .. commandString .. ';'

			file_object:write(commandString .. '\n')
			lasttimePos = newTimePos
		end
	end
end

local updateAwaiting = false

local updateComplete = function()
	updateAwaiting = false
end

local printRecordingStatus = function()
	lasttimePos = (mp.get_property("time-pos") or lasttimePos)
	if file_object ~= nil and lasttimePos ~= nil and startTime ~= nil then
		mp.osd_message(string.format("Recording:%s", SecondsToClock(lasttimePos - startTime)), 10)
	end
end

local updateFilters = function ()
	if not filterIsOn then
		mp.command_native_async({"no-osd", "vf", "add", string.format("@vrrev:%sv360=%s:%s:reset_rot=1:in_stereo=%s:out_stereo=%s:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%s:roll=%.3f:w=%s*192.0:h=%.3f*108.0:h_flip=%s:interp=%s,setsar=sar=%.3f%s",in_flip,inputProjection,outputProjection,in_stereo,out_stereo,idfov,dfov,yaw,pitch,roll,res,res,h_flip,scaling,sarOutput,anaglyph_filter)}, updateComplete)
		filterIsOn=true
	elseif not updateAwaiting then
		updateAwaiting=true
		mp.command_native_async({"no-osd", "vf", "set", string.format("@vrrev:%sv360=%s:%s:reset_rot=1:in_stereo=%s:out_stereo=%s:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%s:roll=%.3f:w=%s*192.0:h=%.3f*108.0:h_flip=%s:interp=%s,setsar=sar=%.3f%s",in_flip,inputProjection,outputProjection,in_stereo,out_stereo,idfov,dfov,yaw,pitch,roll,res,res,h_flip,scaling,sarOutput,anaglyph_filter)}, updateComplete)
	end
	writeHeadPositionChange()
end

local mouse_btn0_cb = function ()
	dragging = not dragging
	if dragging then
		mp.set_property("cursor-autohide", "always")
	else
		mp.set_property("cursor-autohide", saved_props["cursor-autohide"])
	end
end

local mouse_pan = function ()
	if dragging then

		local MousePosx, MousePosy = mp.get_mouse_pos()
		local osd_w, osd_h = mp.get_property("osd-width"), mp.get_property("osd-height")

		local yawpc 	= ((MousePosx/osd_w)-0.5)*180
		local pitchpc   = -((MousePosy/osd_h)-0.5)*180

		local updateCrop = false

		if smoothMouse then
			if yaw ~= yawpc and math.abs(yaw-yawpc)<0.1 then
				yaw = yawpc
				updateCrop=true
				yaw = math.max(-180,math.min(180,yaw))
			elseif yaw ~= yawpc then
				yaw   = (yawpc+(yaw*5))/6
				yaw = math.max(-180,math.min(180,yaw))
				updateCrop=true
			end

			if pitch ~= pitchpc and math.abs(pitch-pitchpc)<0.1 then
				pitch = pitchpc
				pitch = math.max(-180,math.min(180,pitch))
				updateCrop=true
			elseif pitch ~= pitchpc then
				pitch = (pitchpc+(pitch*5))/6
				pitch = math.max(-180,math.min(180,pitch))
				updateCrop=true
			end
		else
			if yaw ~= yawpc then
				yaw  = yawpc
				yaw = math.max(-180,math.min(180,yaw))
				updateCrop=true
			end
			if pitch ~= pitchpc then
				pitch  = pitchpc
				pitch = math.max(-180,math.min(180,pitch))
				updateCrop=true
			end

		end

		if updateCrop then
			updateFilters()
		end

	end
end

local increment_res = function(inc)
	res = res+inc
	res = math.max(math.min(res, 20), 1)
	mp.osd_message(string.format("Out-Width: %spx", res*108.0), 0.5)
	updateFilters()
end

local increment_roll = function (inc)
	roll = roll + inc
	if roll > 180.0 then
		roll = 180.0
	elseif roll < -180.0 then
		roll = -180.0
	end
	mp.osd_message(string.format("Roll: %s°", roll), 0.5)
	updateFilters()
end

local increment_pitch = function (inc)
	pitch = pitch + inc
	if pitch > 180.0 then
		pitch = 180.0
	elseif pitch < -180.0 then
		pitch = -180.0
	end
	mp.osd_message(string.format("Pitch: %s°", pitch), 0.5)
	updateFilters()
end

local increment_yaw = function (inc)
	yaw = yaw+inc
	if yaw > 180.0 then
		yaw = 180.0
	elseif yaw < -180.0 then
		yaw = -180.0
	end
	mp.osd_message(string.format("Yaw: %s°", yaw), 0.5)
	updateFilters()
end

local increment_zoom = function (inc)
	dfov = dfov+inc
	dfov = math.max(math.min(150, dfov), 30)
	mp.osd_message(string.format("D-Fov: %s°", dfov), 0.5)
	updateFilters()
end

local reset_view = function()
	yaw = 0.0
	roll = 0.0
	pitch = 0.0
	mp.osd_message("Reset view.", 0.5)
	updateFilters()
end

local toggleSmoothMouse  = function()
	smoothMouse = not smoothMouse
	if smoothMouse then
		mp.osd_message("Mouse smothing On",0.5)
	else
		mp.osd_message("Mouse smothing Off",0.5)
	end
end

local switchoutputsbs = function()
	if outputMode == 'sbs2l:arcd' then
		out_stereo = '2d'
		outputMode = '2d'
		anaglyph_filter=""
		sarOutput = 1.0
		mp.osd_message("2d output mode")
	elseif outputMode == '2d' then
		out_stereo = 'sbs'
		outputMode = 'sbs'
		anaglyph_filter=""
		sarOutput = 1.0
		mp.osd_message("Side by side full width output mode")
	elseif outputMode == 'sbs' then
		out_stereo = 'sbs'
		outputMode = 'sbs-hw'
		anaglyph_filter=""
		sarOutput = 0.5
		mp.osd_message("side by side half width output mode")
	elseif outputMode == 'sbs-hw' then
		out_stereo = 'sbs'
		outputMode = 'sbs2l:arcg'
		anaglyph_filter=",stereo3d=sbs2l:arcg"
		sarOutput = 0.5
		mp.osd_message("Red cyan gray/monochrome anaglyph output mode")
	elseif outputMode == 'sbs2l:arcg' then
		out_stereo = 'sbs'
		outputMode = 'sbs2l:arbg'
		anaglyph_filter=",stereo3d=sbs2l:arbg"
		sarOutput = 0.5
		mp.osd_message("Red blue gray/monochrome anaglyph output mode")
	elseif outputMode == 'sbs2l:arbg' then
		out_stereo = 'sbs'
		outputMode = 'sbs2l:arcd'
		anaglyph_filter=",stereo3d=sbs2l:arcd"
		sarOutput = 0.5
		mp.osd_message("Red cyan dubois anaglyph output mode")
	end
	updateFilters()
end

local switchScaler = function()
	if scaling == 'nearest' then
		scaling = 'cubic'
	elseif scaling == 'cubic' then
		scaling = 'lagrange9'
	elseif scaling == 'lagrange9' then
		scaling = 'lanczos'
	elseif scaling == 'lanczos' then
		scaling = 'linear'
	elseif scaling == 'linear' then
		scaling = 'nearest'
	end
	mp.osd_message("Scaling algorithm: " .. scaling,5.5)
	updateFilters()
end

local switchEye = function()
	if h_flip == '0' then
		h_flip  = '1'
		in_flip = 'hflip,'
		mp.osd_message("Right eye",0.5)
	else
		h_flip  = '0'
		in_flip = ''
		mp.osd_message("Left eye",0.5)
	end
	print(in_flip,h_flip)
	updateFilters()
end

local cycleInputProjection = function()
	inputProjectionInd = ((inputProjectionInd+1) % (#inputProjections +1))
	inputProjection    = inputProjections[inputProjectionInd]
	mp.osd_message(string.format("Input projection: %s ",inputProjection),0.5)
	updateFilters()
end

local cycleOutputProjection = function()
	outputProjectionInd = ((outputProjectionInd+1) % (#outputProjections + 1))
	outputProjection    = outputProjections[outputProjectionInd]
	mp.osd_message(string.format("Output projection: %s",outputProjection),0.5)
	updateFilters()
end

local switchInputFovBounds = function()
	if idfov == 180.0 then
			idfov = 360.0
	elseif idfov == 360.0 then
		idfov = 90.0
	else
		idfov = 180.0
	end
	mp.osd_message(string.format("Input fov bounds: %s°",idfov),0.5)
	updateFilters()
end

local switchStereoMode = function()
	if in_stereo == 'sbs' then
		in_stereo = 'tb'
	else
		in_stereo = 'sbs'
	end
	mp.osd_message("Input format: " .. in_stereo,0.5)
	updateFilters()
end

local binding_by_name = function(lookup)
	for k, v in pairs(bindings) do
		if v["name"] == lookup then
			return k
		end
	end
end

local build_help_string = function()
	return table.concat({ "Default keyboard & mouse controls:\n",
	binding_by_name("show_help"), " = show help\n",
	binding_by_name("res_up"), ",", binding_by_name("res_down"), " = adjust quality\n",
	"Mouse Click = look around\n",
	binding_by_name("roll_left"), ",", binding_by_name("roll_right"), " = roll head\n",
	"Mouse Wheel = zoom\n",
	binding_by_name("switch_stereo"),	" = switch stereo Mode\n",
	binding_by_name("switch_eye"), 		" = switch eye side\n",
	binding_by_name("switch_scaler"), 	" = switch scaler\n",
	binding_by_name("switch_output_sbs"), 	" = toggle side by side output\n",
	binding_by_name("toggle_smooth"), 	" = toggle mouse smoothing\n",
	binding_by_name("new_log_session"),	" = start/stop motion recording\n",
	binding_by_name("cycle_input"), ",", binding_by_name("cycle_output"), " = cycle in and out projections\n",
	binding_by_name("reset_view"), " = center view\n"
	})
end

local help_string = nil

local showHelp = function()
	if help_string == nil then
		help_string = build_help_string()
	end
	mp.osd_message(help_string, 10)
end

local closeCurrentLog = function()
	commandForFinalLog=''
	if lasttimePos ~= nil and file_object ~= nil then

		finalTimeStamp = mp.get_property("time-pos")
		-- Can be nil while the player is shutting down and the file is already closed
		if finalTimeStamp == nil then
			finalTimeStamp = lasttimePos
		end

		file_object:write('#\n')

 		local stats = string.format('# Duration: %s-%s (total %s) %s seconds',
			SecondsToClock(startTime),
			SecondsToClock(finalTimeStamp),
			SecondsToClock(finalTimeStamp - startTime),
			finalTimeStamp - startTime
		)

		print('#')
		file_object:write(stats .. '\n')
		print(stats)

		file_object:write('# Suggested ffmpeg conversion command:\n')

		local closingCommandComment = string.format(
			'ffmpeg -y -ss %s -i "%s" -to %s -copyts -filter_complex "%sv360=%s:%s:in_stereo=%s:out_stereo=%s:reset_rot=1:id_fov=%s:d_fov=%.3f:yaw=%.3f:pitch=%.3f:roll=%.3f:w=1920.0:h=1080.0:interp=cubic:h_flip=%s,setsar=sar=%.3f,sendcmd=filename=%s_3dViewHistory_%s.txt" -avoid_negative_ts make_zero -preset slower -crf 17 "%s_2d_%03d.mp4"',
			startTime,filename,finalTimeStamp,in_flip,inputProjection,outputProjection,in_stereo,out_stereo,idfov,init_dfov,init_yaw,init_pitch,init_roll,h_flip,videofilename,fileobjectNumber,videofilename,fileobjectNumber,sarOutput
		)

		file_object:write('# ' .. closingCommandComment .. '\n')
		file_object:write('#\n')

		print(closingCommandComment)
		print('#')

		commandForFinalLog = closingCommandComment
	end
	lasttimePos = nil
	startTime = nil
	if file_object ~= nil then
		file_object:close()
		file_object = nil
	end
	return commandForFinalLog
end

local startNewLogSession = function()
	if file_object == nil then
		openNewLogFile()
		writeHeadPositionChange()
		mp.osd_message(string.format("Started Motion Record %s_3dViewHistory_%s.txt",videofilename,fileobjectNumber), 0.5)
	else
		mp.osd_message(string.format("Stopped Motion Record %s_3dViewHistory_%s.txt",videofilename,fileobjectNumber), 2.5)
		writeHeadPositionChange()
		local command = closeCurrentLog()
		if command then
			ffmpegCommandList[#ffmpegCommandList+1] = command
		end
	end
end

local onExit = function()
	closeCurrentLog()
	mergedCommand = ''
	for _, v in pairs(ffmpegCommandList) do
		if v ~= '' then
			mergedCommand = mergedCommand .. ' & ' .. v
		end
	end
	if mergedCommand ~= '' then
		mergedCommand = mergedCommand:sub(3)
		print(mergedCommand)
		batchfile = io.open('convert_3dViewHistory.bat', 'w')
		batchfile:write(mergedCommand)
		batchfile:close()
		print('Batch processing file created convert_3dViewHistory.bat')
	end
end

local save_props = function()
	for k, _ in pairs(saved_props) do
		local propv = (mp.get_property(k) or "NIL")
		saved_props[k] = propv

		if k == "hwdec" and propv ~= "no" then
			-- Workaround: hardware acceleration rarely works well, so we have to disable it.
			-- Error: [ffmpeg] Impossible to convert between the formats supported by
			-- the filter 'mpv_src_default_in' and the filter 'auto_scaler_0'
			mp.osd_message("Temporarily turning off hardware decoding.", 1.5)
			mp.set_property("hwdec", "no")
		end
	end
end

local restore_props = function()
	for k, v in pairs(saved_props) do
		if v ~= "NIL" then
			if k == "hwdec" then
				-- Can also be displayed in osd console with "show-text ${hwdec-current}"
				mp.osd_message(string.format("Restoring hardware acceleration: %s", v), 1.5)
			end
			mp.set_property(k, v)
		end
	end

	for k, _ in pairs(saved_props) do
		saved_props[k] = "NIL"
	end
end

local restore_keybinds = function()
	-- There is no need to re-apply the previous key-bindings if they have been
	-- forcibly rebound by our script. They are still there after removal.
	for k,v in pairs(bindings) do
		if k == binding_by_name("toggle_vr360") then
			print("Keeping key bind to toggle vr360: " .. k)
		else
			mp.remove_key_binding(v["name"])
		end
	end
end

local recordingStatusTimer = nil

local initFunction = function()
	save_props()
	for key, pref in pairs(bindings) do
		mp.add_forced_key_binding(key, pref["name"], pref["fn"], pref["flags"])
	end

	mp.set_property("osc", opts["osc"])
	-- It seems not forcing fullscreen may cause minor issues. Use with caution.
	mp.set_property("fullscreen", opts["fullscreen"])
	mp.set_property("osd-font-size", opts["osd-font-size"])

	mp.register_event("end-file", onExit)
	mp.register_event("shutdown", onExit)

	recordingStatusTimer = mp.add_periodic_timer(0.1, printRecordingStatus)

	updateFilters()
end

local teardownFunction = function()
	if recordingStatusTimer ~= nil and recordingStatusTimer:is_enabled() then
		recordingStatusTimer:kill()
		recordingStatusTimer = nil
	end
	filterIsOn = false
	updateAwaiting = true
	mp.unregister_event(onExit)
	onExit()
	-- Remove bindings before to avoid updating the filter with mouse movements
	restore_keybinds()
	-- Remove filter before restoring hardware acceleration to avoid potential errors
	mp.command_native({"no-osd", "vf", "remove", "@vrrev"}, updateComplete)
	restore_props()
end

local toggleVR = function()
	if not opts.enabled then
		opts.enabled = true
		initFunction()
		return
	end
	teardownFunction()
	opts.enabled = false
end

bindings = {
	[opts.toggle_vr360]		=	{name="toggle_vr360",		fn=toggleVR				},
	[opts.cycle_input]		=	{name="cycle_input",		fn=cycleInputProjection },
	[opts.cycle_output]		=	{name="cycle_output",		fn=cycleOutputProjection },
	[opts.roll_left]		=	{name="roll_left",			fn=function() increment_roll(-1) end,	flags={repeatable=true}},
	[opts.roll_right]		=	{name="roll_right",			fn=function() increment_roll(1) end,	flags={repeatable=true}},
	[opts.write_log]		=	{name="write_log",			fn=writeHeadPositionChange },
	[opts.pitch_up]			=	{name="pitch_up",			fn=function() increment_pitch(1) end,	flags={repeatable=true}},
	[opts.pitch_down]		=	{name="pitch_down",			fn=function() increment_pitch(-1) end,	flags={repeatable=true}},
	[opts.yaw_up]			=	{name="yaw_up",				fn=function() increment_yaw(1) end,		flags={repeatable=true}},
	[opts.yaw_down]			=	{name="yaw_down",			fn=function() increment_yaw(-1) end,	flags={repeatable=true}},
	[opts.easy_crop]		=	{name="easy_crop",			fn=updateFilters,						flags={repeatable=true}},
	[opts.res_up]			=	{name="res_up",				fn=function() increment_res(1) end,		flags={repeatable=true}},
	[opts.res_down]			=	{name="res_down",			fn=function() increment_res(-1) end,	flags={repeatable=true}},
	[opts.zoom_in]			=	{name="zoom_in",			fn=function() increment_zoom(-1) end,	flags={repeatable=true}},
	[opts.zoom_out]			=	{name="zoom_out",			fn=function() increment_zoom(1) end,	flags={repeatable=true}},
	[opts.wzoom_out]		=	{name="wzoom_out",			fn=function() increment_zoom(1) end },
	[opts.wzoom_in] 		=	{name="wzoom_in",			fn=function() increment_zoom(-1) end },
	[opts.reset_view] 		=	{name="reset_view",			fn=reset_view 			},
	[opts.switch_stereo]	=	{name="switch_stereo",		fn=switchStereoMode		},
	[opts.switch_eye]		=	{name="switch_eye",			fn=switchEye			},
	[opts.switch_scaler]	=	{name="switch_scaler",		fn=switchScaler			},
	[opts.switch_output_sbs]=	{name="switch_output_sbs",	fn=switchoutputsbs		},
	[opts.toggle_smooth]	=	{name="toggle_smooth",		fn=toggleSmoothMouse	},
	[opts.switch_bounds]	=	{name="switch_bounds",		fn=switchInputFovBounds	},
	[opts.new_log_session]	=	{name="new_log_session",	fn=startNewLogSession	},
	[opts.grab_mouse]		=	{name="grab_mouse",			fn=mouse_btn0_cb		},
	[opts.mouse_pan]		=	{name="mouse_pan",			fn=mouse_pan			},
	[opts.show_help]		=	{name="show_help",			fn=showHelp				}
}

local register_toggle_key = function ()
	-- mp.add_forced_key_binding("v", "toggle_vr360", toggleVR)
	for k,v in pairs(bindings) do
		if v["name"] == "toggle_vr360" then
			mp.add_forced_key_binding(k, "toggle_vr360", v["fn"])
			return
		end
	end
end

register_toggle_key()

if opts.enabled == true then
	initFunction()
end


text:
# VR-reversal

Uses MPV and a plugin to play a 3D side-by-side video as a 2D video, allows you to look around and zoom within the video, optionally logs the head motions to a file for later rendering out to a 2D video with ffmpeg.

![Example output](https://github.com/dfaker/VR-reversal/blob/master/example.gif?raw=true)

# Installation and Usage:

- Download the lastest MPV https://mpv.io/
- Download the [360plugin.lua](https://raw.githubusercontent.com/dfaker/VR-reversal/master/360plugin.lua) plugin from this repo.
- Play a video using the plugin with the command: `mpv --script=360plugin.lua --script-opts=360plugin-enabled=yes videoFile.mp4`

If you want to enable this script automatically on startup in your regular MPV, you can place the config file from the `script-opts` directory in the corresponding MPV config directory, along with the script itself in MPV's `scripts` directory. To activate auto-starting of the script every MPV session, change `enabled=yes` in the config file.

If you want to save the 2D versions videos rather than just watch them you'll need a recent version of ffmpeg from https://ffmpeg.org/ but it's not needed just for viewing.

Alternatively rather than using the command line, Microsoft Windows users may choose to:

- Place mpv.exe, [vr-reversal.bat](https://raw.githubusercontent.com/dfaker/VR-reversal/master/vr-reversal.bat) and [360plugin.lua](https://raw.githubusercontent.com/dfaker/VR-reversal/master/360plugin.lua) (the latter two are available as a Zip file in [releases](https://github.com/dfaker/VR-reversal/releases)) in the same directory.
- Run `vr-reversal.bat`
- Drag and drop videos onto the MPV window.

# Controls

You can press `?` to show all of the keyboard controls on screen at any time.

When the player is started, if the script is automatically enabled, you'll be looking straight forwards. If not type:

- `v` to toggle the main feature on or off.

The video will start at a low resolution, if you'd like more detail press `y` increase the initial preview quality `h` to reduce it again.

- `y` Increase resolution
- `h` decrease resolution

Control where you're looking with the mouse:

- MouseLook: click anywhere in the video and your mouse position will control the camera, click again to stop mouse control
- MouseScroll: zoom in and out

or alternately look around with these keys:

- `i`,`j`,`k`,`l` look around
- `u`,`o` roll head
- `=`,`-` zoom
- `TAB` center view

Additional controls:

- `t` switch the eye you're looking through between left and right
- `e` switch the video scaler between nearest neighbour and bicubic
- `g` toggle mouse smoothing
- `n` start / stop logging head motions to file (for later rendering)
- `?` display reminder of keyboard and mouse controls

Advanced projection controls:

90% of modern vr releases work perfectly with the defauls of 180 degree 'hequirect' projection so you shound't need these unless playing older or unusually formatted content:

- `r` toggle stereo mode between top/bottom and side by-side
- `b` cycle input fov bounds between 180,360 and 90
- `1` cycle through input projections
- `2` cycle through output projections
- `p` cycle through 2D output modes including flat 2D, reprojected side by side and anaglyph modes.

Most of the standard default MPV controls are maintained:

- `Arrow keys` seek through video
- `SPACE` pause
- `f` fullscreen toggle
- `9`,`0` or `/`,`*` volume up and down
- `m` mute

And finally when you are done:

- `q` quit

You can configure the default keybindings in the `script-opts/360plugin.conf` file, or override them in your `input.conf` file as usual.

# 'Head' Motion Logging and saving your clips
If you have pressed `n` during your session your 'head' movements in the video will be logged to a file named `{originalFilename}_3dViewHistory_{sectionNumber}.txt` this is in the format of motion commands to be processed by ffmpeg https://ffmpeg.org/

The script will create a batch file `convert_3dViewHistory.bat` after you exit the player, if you have ffmpeg installed you may simply run this file to perform the conversion to 2D .mp4 clips automatically.


text:
enabled=no
cycle_input=1
cycle_output=2
roll_left=u
roll_right=o
write_log=w
pitch_up=i
pitch_down=k
yaw_up=l
yaw_down=j
easy_crop=c
res_up=y
res_down=h
zoom_in==
zoom_out=-
wzoom_out=WHEEL_DOWN
wzoom_in=WHEEL_UP
reset_view=TAB
switch_stereo=r
switch_eye=t
switch_scaler=e
toggle_smooth=g
switch_bounds=b
new_log_session=n
grab_mouse=mouse_btn0
mouse_pan=mouse_move
show_help=?
toggle_vr360=v
osc=no
fullscreen=yes
osd-font-size=30


text:
package main

import (
	"fmt"
	"github.com/ricochet2200/go-disk-usage/du"
)

var KB = uint64(1024)

func main() {
	usage := du.NewDiskUsage(".")
	fmt.Println("Free:", usage.Free()/(KB*KB))
	fmt.Println("Available:", usage.Available()/(KB*KB))
	fmt.Println("Size:", usage.Size()/(KB*KB))
	fmt.Println("Used:", usage.Used()/(KB*KB))
	fmt.Println("Usage:", usage.Usage()*100, "%")
}

repo_language: Go


text:
go-disk-usage
=============

Get disk usage information like how much space is available, free, and used.

## Documentation
Documentation can be found [here](https://godoc.org/github.com/ricochet2200/go-disk-usage/du) and there is a [short example](https://github.com/ricochet2200/go-disk-usage/blob/master/duexample.go) of how to use this library.

## Compatibility
This works for Windows, MacOS, and Linux although there may some minor variability between what this library reports and what you get from `df`.  This library will maintain reverse compatability, any breaking changes will be made to a forked repository.

## Install

```bash
go get -u "github.com/ricochet2200/go-disk-usage/du"
```

## Usage

```go
import "github.com/ricochet2200/go-disk-usage/du"
usage := du.New("/path/to")
```


text:
// +build !windows

package du

import "syscall"

// DiskUsage contains usage data and provides user-friendly access methods
type DiskUsage struct {
	stat *syscall.Statfs_t
}

// NewDiskUsages returns an object holding the disk usage of volumePath
// or nil in case of error (invalid path, etc)
func NewDiskUsage(volumePath string) *DiskUsage {

	var stat syscall.Statfs_t
	syscall.Statfs(volumePath, &stat)
	return &DiskUsage{&stat}
}

// Free returns total free bytes on file system
func (du *DiskUsage) Free() uint64 {
	return du.stat.Bfree * uint64(du.stat.Bsize)
}

// Available return total available bytes on file system to an unprivileged user
func (du *DiskUsage) Available() uint64 {
	return du.stat.Bavail * uint64(du.stat.Bsize)
}

// Size returns total size of the file system
func (du *DiskUsage) Size() uint64 {
	return uint64(du.stat.Blocks) * uint64(du.stat.Bsize)
}

// Used returns total bytes used in file system
func (du *DiskUsage) Used() uint64 {
	return du.Size() - du.Free()
}

// Usage returns percentage of use on the file system
func (du *DiskUsage) Usage() float32 {
	return float32(du.Used()) / float32(du.Size())
}


text:
module github.com/ricochet2200/go-disk-usage/du

go 1.15


text:
package du

import (
	"fmt"
	"testing"
)

var KB = uint64(1024)

func TestNewDiskUsage(t *testing.T) {
	usage := NewDiskUsage(".")
	fmt.Println("Free:", usage.Free()/(KB*KB))
	fmt.Println("Available:", usage.Available()/(KB*KB))
	fmt.Println("Size:", usage.Size()/(KB*KB))
	fmt.Println("Used:", usage.Used()/(KB*KB))
	fmt.Println("Usage:", usage.Usage()*100, "%")
}


text:
package du

import (
	"syscall"
	"unsafe"
)

type DiskUsage struct {
	freeBytes  int64
	totalBytes int64
	availBytes int64
}

// NewDiskUsages returns an object holding the disk usage of volumePath
// or nil in case of error (invalid path, etc)
func NewDiskUsage(volumePath string) *DiskUsage {

	h := syscall.MustLoadDLL("kernel32.dll")
	c := h.MustFindProc("GetDiskFreeSpaceExW")

	du := &DiskUsage{}

	c.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(volumePath))),
		uintptr(unsafe.Pointer(&du.freeBytes)),
		uintptr(unsafe.Pointer(&du.totalBytes)),
		uintptr(unsafe.Pointer(&du.availBytes)))

	return du
}

// Free returns total free bytes on file system
func (du *DiskUsage) Free() uint64 {
	return uint64(du.freeBytes)
}

// Available returns total available bytes on file system to an unprivileged user
func (du *DiskUsage) Available() uint64 {
	return uint64(du.availBytes)
}

// Size returns total size of the file system
func (du *DiskUsage) Size() uint64 {
	return uint64(du.totalBytes)
}

// Used returns total bytes used in file system
func (du *DiskUsage) Used() uint64 {
	return du.Size() - du.Free()
}

// Usage returns percentage of use on the file system
func (du *DiskUsage) Usage() float32 {
	return float32(du.Used()) / float32(du.Size())
}


text:
## Getting Started

You can easily get started with play-clj by creating a new project with [Leiningen](https://github.com/technomancy/leiningen):

    lein new play-clj hello-world

## Project Structure

After making a game called _hello-world_, you'll see two sub-folders: `android` and `desktop`. You'll be spending most of your time in the `desktop` folder, because it's easier to develop your game on your computer and build it for mobile devices later.

Your actual game code will be in the `desktop/src-common` folder, and all the images and sound files will be in the `desktop/resources` folder. When you're ready to build an Android version, it will read from both of these folders, so you don't have to duplicate any files.

## Your First Run

In your terminal, go to the `desktop` directory and do `lein run`. If all goes well, you should see a window with nothing but a small label on the bottom left that says "Hello world!".

## Game Structure

Open the main file at `desktop/src-common/hello_world/core.clj` in your editor of choice. Let's look at the basic structure of your game. It starts out with a call to `defgame`, which should only be used once as it initializes the game for you. It contains a single function called `:on-create` that runs when your game starts. The only thing it needs to do is hand off to your screen, where all the action takes place.

In `defscreen`, you'll find that a few simple functions are defined: `:on-show` and `:on-render`. The first only runs when the screen is first shown, and the second is run every single time your game wants to draw on the screen (which is ideally 60 times per second).

There are many other functions you can put inside `defscreen`, each letting you run code when certain important events happen. For now, we'll stick to the two we started with, because they are the most fundamental, but you can read [the documentation](http://oakes.github.io/play-clj/core.defscreen.html) to learn about the others.

## Entity System

Most games need some way to keep track of all the things displayed within them. We call these things "entities". Normally, we need to remember attributes such as their position, size, and possibly other values like health and damage. In play-clj, these entities are simply maps, so you can store whatever you want inside of them.

Often, games will store these entities in a list, and in their render function they will loop over the list, modify the entities (such as moving them), and then call a function to render them. With functional programming, on the other hand, we want to avoid directly mutating values, as it leads to more complicated and error-prone software.

Instead, play-clj stores the entities vector behind the scenes and passes it to each function within `defscreen`. It's a normal Clojure vector, so you can't directly change it. Instead, you must return a new entities vector at the end of each `defscreen` function, which will then be provided to all other functions when they run.

## Loading a Texture

Right now, you're using the `play-clj.ui` library to display a label. This library is useful for typical UI needs such as a title screen, but not very useful for the game itself. Let's get rid of it for now, and instead use the `play-clj.g2d` library, which contains the basic functions for 2D games. Try changing the `ns` declaration to look like this:

```clojure
(ns hello-world.core
  (:require [play-clj.core :refer :all]
            [play-clj.g2d :refer :all]))
```

Now let's find an image to use as a texture in the game. Find one you'd like to use, such as [this Clojure logo](https://upload.wikimedia.org/wikipedia/commons/8/85/Clojure-icon.png), and save it to the `desktop/resources` folder. Next, simply change the line where the label entity is being created, so it creates a texture from that file instead:

```clojure
    (texture "Clojure-icon.png")
```

## Size and Position

If you run the code now, you'll see the image in the bottom-left corner. As mentioned, entities such as the one created by `texture` are simply Clojure records. By default, our entity will look like this:

```clojure
#play_clj.entities.TextureEntity{:object #<TextureRegion com.badlogic.gdx.graphics.g2d.TextureRegion@4634b96>}
```

A `texture` contains the underlying Java object. By default, it will be drawn at the bottom-left corner with the size of the image itself. You can change the position and size by simply using `assoc`:

```clojure
    (assoc (texture "Clojure-icon.png")
           :x 50 :y 50 :width 100 :height 100)
```

You can also set scaling and rotation on a texture using :scale-x, :scale-y, and :angle, which all use (:origin-x, :origin-y) as the center. For example, here we rotate it 45 degrees counter-clockwise around the bottom-left corner:

```clojure
    (assoc (texture "Clojure-icon.png")
           :x 50 :y 50 :width 100 :height 100
           :angle 45 :origin-x 0 :origin-y 0)
```

## Input

Let's add a new function at the end of `defscreen` called `:on-key-down`, which runs when a key is pressed:

```clojure
  :on-key-down
  (fn [screen entities]
    )
```

If takes the same form as the other functions, expecting a new entities vector to be returned at the end. The first argument, `screen`, which we haven't talked about yet, is a Clojure map containing various important values. In the `:on-key-down` function, it will contain a `:key` which indicates what key was pressed.

To figure out what key it refers to, you'll need to compare it to a known key code, which you can get with `key-code`. See [the documentation](http://oakes.github.io/play-clj/core.key-code.html) or the example below to learn how to use it.

Let's write a conditional statement that prints out which arrow key you pressed. Note that if a `defscreen` function returns `nil`, it leaves the entities vector unchanged, so the code below won't wipe out the entities vector.

```clojure
  :on-key-down
  (fn [screen entities]
    (cond
      (= (:key screen) (key-code :dpad-up))
      (println "up")
      (= (:key screen) (key-code :dpad-down))
      (println "down")
      (= (:key screen) (key-code :dpad-right))
      (println "right")
      (= (:key screen) (key-code :dpad-left))
      (println "left")))
```

Now, what about mobile devices? We may not have a keyboard, so let's create an `:on-touch-down` function:

```clojure
  :on-touch-down
  (fn [screen entities]
    )
```

The [game](http://oakes.github.io/play-clj/core.game.html) function gives you convenient access to the window dimensions and the x/y position of the pointer:

```clojure
  :on-touch-down
  (fn [screen entities]
    (cond
      (> (game :y) (* (game :height) (/ 2 3)))
      (println "up")
      (< (game :y) (/ (game :height) 3))
      (println "down")
      (> (game :x) (* (game :width) (/ 2 3)))
      (println "right")
      (< (game :x) (/ (game :width) 3))
      (println "left")))
```

Conveniently, the `:on-touch-down` function also runs when a mouse is clicked on the screen, so we are adding mouse support to the game as well!

## Movement

We already know how to change an entity's position, so let's leverage that to make our image move when we hit the keys. Make a new function above `defscreen` that takes the entity and a keyword, and returns the entity with an updated position:

```clojure
(defn move
  [entity direction]
  (case direction
    :down (update entity :y dec)
    :up (update entity :y inc)
    :left (update entity :x dec)
    :right (update entity :x inc)
    nil))
```

Now we can update our `:on-key-down` and `:on-touch-down` functions to move the entity. Note that we are technically returning a single entity rather than an entities vector, but play-clj will turn it back into a vector automatically.

```clojure
  :on-key-down
  (fn [screen entities]
    (cond
      (= (:key screen) (key-code :dpad-up))
      (move (first entities) :up)
      (= (:key screen) (key-code :dpad-down))
      (move (first entities) :down)
      (= (:key screen) (key-code :dpad-right))
      (move (first entities) :right)
      (= (:key screen) (key-code :dpad-left))
      (move (first entities) :left)))

  :on-touch-down
  (fn [screen entities]
    (cond
      (> (game :y) (* (game :height) (/ 2 3)))
      (move (first entities) :up)
      (< (game :y) (/ (game :height) 3))
      (move (first entities) :down)
      (> (game :x) (* (game :width) (/ 2 3)))
      (move (first entities) :right)
      (< (game :x) (/ (game :width) 3))
      (move (first entities) :left)))
```

## Camera

You'll notice that when you resize your game's window, the image looks stretched. That's because the game still thinks it's 800x600 pixels in size, so it stretches accordingly. To make your game adjust its ratio for different screen sizes, you need to use a camera.

First, you need to create a camera and add it to the screen map in the `:on-show` function, like this:

```clojure
    (update! screen :renderer (stage) :camera (orthographic))
````

Orthographic cameras are for 2D games, so that's what we're using. Now, we need to create a new `defscreen` function called `:on-resize`, which will run whenever the screen resizes:

```clojure
  :on-resize
  (fn [screen entities]
    )
```

Lastly, you'll need to make either the width or height of the screen a constant value, so the other dimension can adjust to keep a constant ratio. We'll make the screen's height a constant 600 units in size using the `height!` function, which returns `nil` so the entities vector won't be changed.

```clojure
  :on-resize
  (fn [screen entities]
    (height! screen 600))
```

Now, when you resize your game, the image is no longer stretched!

## Timers

It is often necessary to do something in the future or at regular intervals. For this, we have `add-timer!` and its companion, `remove-timer!`. Let's suppose you want to spawn a new enemy exactly 10 seconds after the game begins. First, add the timer in the beginning of the `:on-show` function:

```clojure
    (add-timer! screen :spawn-enemy 10)
```

Then, add the `:on-timer` function to your screen:

```clojure
  :on-timer
  (fn [screen entities]
    )
```

If you want `:on-timer` to run at a regular 2-second interval, just add that as an argument:

```clojure
    (add-timer! screen :spawn-enemy 10 2)
```

If you want it to run exactly 20 times, you can add one final argument to specify how many times it should repeat after the first run:

```clojure
    (add-timer! screen :spawn-enemy 10 2 19)
```

Of course, you can add more than one timer. The id you supply them will be supplied to the `:on-timer` function's screen map:

```clojure
  :on-timer
  (fn [screen entities]
    (case (:id screen)
      :spawn-enemy (conj entities (create-enemy))
      :spawn-friend (conj entities (create-friend))
      nil))
```

Lastly, at any time you can remove a timer:

```clojure
    (remove-timer! screen :spawn-enemy)
```

## Java Interop

At some point, you will need to do more than simple positioning and sizing. For that, you'll need to call libGDX methods directly. You could, of course, use Clojure's [Java interop](http://clojure.org/java_interop) syntax on the `:object` contained within the entity. This is a bit ugly, though, and requires you to do all the importing and type hinting yourself.

In play-clj, many different calls, such as `texture`, are actually macros that allow you to call the underlying Java methods after the required argument(s). In this case, the underlying class is called [TextureRegion](http://libgdx.badlogicgames.com/nightlies/docs/api/com/badlogic/gdx/graphics/g2d/TextureRegion.html). Consider this:

```clojure
    (texture "Clojure-icon.png" :flip true false)
```

...which is transformed into:

```clojure
    (let [entity (texture "Clojure-icon.png")]
      (doto ^TextureRegion (:object entity)
        (.flip true false))
      entity)
```

You can even call multiple methods in the same expression this way. For example:

```clojure
    (texture "Clojure-icon.png"
             :flip true false
             :set-region 0 0 100 100)
```

...which is transformed into:

```clojure
    (let [entity (texture "Clojure-icon.png")]
      (doto ^TextureRegion (:object entity)
        (.flip true false)
        (.setRegion 0 0 100 100))
      entity)
```

There is also an equivalent macro with a `!` on the end, which lets you call these methods on an existing entity:

```clojure
    (texture! entity :flip true false)
```

In this case, you can only include a single method call, because it's also meant to be a simple way to call getter methods that return a value. For example:

```clojure
    (texture! entity :get-region-width)
```

Lastly, there is one final version with a `*` at the end. Essentially, `texture*` is the function version of `texture`. It has the same required arguments, but it can't do any of the Java interop stuff noted above. This is useful because sometimes you may want to pass `texture` around as a function, such as in the first argument of `map`.

If you try that, you'll get the dreaded error: `java.lang.RuntimeException: Can't take value of a macro`. Macros run at compile time, so you can't pass them around like functions. The solution is to use the `*` version, which is indeed a function:

```clojure
    (map texture* ["image1.png" "image2.png" "image3.png"])
```

## Multiple Screens

It is possible to have multiple screens for your game. You may want a title screen at first, and then go to your game when an item is clicked. You can do this by simply calling the same `set-screen!` function that is run in `defgame`. You'll need to declare your symbols at the top of your file, so you can refer to them from anywhere below.

`(declare hello-world title-screen main-screen)`

Now you are free to call `(set-screen! hello-world main-screen)` inside your `title-screen` if you wish. You may also call it inside `main-screen` itself, which is useful if you want to restart the game!

You may want to display two different screens at once. This is useful in situations where you want to overlay something on your game that you want to remain fixed and independent of the game's camera. For example, to display a label with the current frames per second, create another screen like this:

```clojure
(defscreen text-screen
  :on-show
  (fn [screen entities]
    (update! screen :camera (orthographic) :renderer (stage))
    (assoc (label "0" (color :white))
           :id :fps
           :x 5))

  :on-render
  (fn [screen entities]
    (->> (for [entity entities]
           (case (:id entity)
             :fps (doto entity (label! :set-text (str (game :fps))))
             entity))
         (render! screen)))

  :on-resize
  (fn [screen entities]
    (height! screen 300)))
```

Then, in `defgame`, set the screens in the order in which you'd like them to appear:

```clojure
(defgame hello-world-game
  :on-create
  (fn [this]
    (set-screen! this main-screen text-screen)))
```

Make sure you add `play-clj.ui` back to your `ns` declaration, so you can use the label again. Also, note that only the first screen, which in this case is `main-screen`, calls `(clear!)` in its `:on-render` function. If `text-screen` called it as well, it would clear whatever was drawn by `main-screen`.

With multiple screens being displayed, it will often be important to make them interact. For example, you may want to place a button on `text-screen` that causes a character on `main-screen` to change color. You can do this with the [screen!](http://oakes.github.io/play-clj/core.screen!.html) function.

First, define a custom screen function in `main-screen` with a name such as `:on-change-color`, where you can write the code that changes the character's color. Then, in `text-screen`, listen for the button click (using the `:on-ui-changed` screen function) and manually run the custom function. You may optionally provide key-value pairs that will be passed into its screen map:

```clojure
(screen! main-screen :on-change-color :color :blue)
```

## Using the REPL

It is much faster to develop a game while it's running, and that's what the Clojure REPL lets you do. To get started, just hit the _Run with REPL_ button in the build pane. When it launches, type `(-main)` into the prompt and hit enter, and your game will launch.

Then, switch back to your code while the game is still running. Let's modify `:on-key-down` so the left arrow makes it go right, and vice versa, by swiching the keyword you pass into `move`:

```clojure
      (= (:key screen) (key-code :dpad-right))
      (move (first entities) :left)
      (= (:key screen) (key-code :dpad-left))
      (move (first entities) :right)
```

Now save the file and hit _Reload_ in the build pane. Now try it out! The key bindings have been changed while the game is still running. Now switch the keywords back and reload again.

Keep in mind that the `:on-show` function only runs when the screen first shows. Therefore, if you want to modify something there and see the result, after you hit _Reload_ you'll need to restart the screen.

First, switch the REPL into the right namespace by typing `(in-ns 'hello-world.core)` and hitting enter. Then, type the following command, which runs the `set-screen!` function on the GL thread in order to restart `main-screen`:

`(on-gl (set-screen! hello-world-game main-screen))`

The next thing to try is reading and modifying state. We'll be using some REPL-specific functions, so type `(use 'play-clj.repl)` to bring them in. Let's peek into the entities vector by typing the following into the REPL:

`(e main-screen)`

That should print out a vector with a single map inside of it. Now try moving your image and then run the command again. The `:x` and `:y` values should now be updated. You're looking at your game in real-time! Lastly, let's try moving the entity from the REPL:

`(e! identity main-screen :x 200 :y 200)`

Lastly, one common issue people have with play-clj in a REPL is that it if you make an error that causes an exception, it's difficult to restart the game after fixing it. To deal with this, there is the `set-screen-wrapper!` function, which lets you catch errors that occur in any screen function and handle them gracefully. For example, here we make it switch to a blank screen if any error occurs:

```clojure
(defscreen blank-screen
  :on-render
  (fn [screen entities]
    (clear!)))

(set-screen-wrapper! (fn [screen screen-fn]
                       (try (screen-fn)
                         (catch Exception e
                           (.printStackTrace e)
                           (set-screen! hello-world-game blank-screen)))))
```

Note that this will only catch runtime errors, not compile errors such as misspelled symbols (which don't affect your game when they occur). After fixing the issue, you can switch back to `main-screen` from your REPL with `(on-gl (set-screen! hello-world-game main-screen))`.

repo_name: oakes/play-clj

repo_language: Clojure