spotify working

This commit is contained in:
Errol Sancaktar 2024-08-19 17:03:43 -06:00
parent 15361dc75c
commit 4535f8ac86
2039 changed files with 844170 additions and 30 deletions

View File

@ -922,10 +922,10 @@ hide_when_typing = true
[shell]
program="/usr/bin/tmux"
program="/opt/homebrew/bin/tmux"
args=['new-session', '-A', '-s', 'default']
[font]
normal = { family = "DejaVu Sans Mono", style = "Normal" }
normal = { family = "Hack Nerd Font Mono" }
size = 13
builtin_box_drawing = true

View File

@ -16,6 +16,7 @@ declare -A alias_list=(
# PYTHON
[act]='source venv/bin/activate'
[p]='ipython'
[dea]='deactivate'
# GIT
[gaa]='git add --all'

View File

@ -0,0 +1 @@
/Users/errol/Library/Application Support/OrcaSlicer

View File

@ -55,7 +55,7 @@ bind Space kill-pane # Kill Active pane
bind c new-window -c "#{pane_current_path}" # Keep Path
bind -n C-Space last-window
bind -n C-f last-window
bind - split-window -v
@ -112,8 +112,11 @@ set -g status-right '#[fg=white]#(hostname)@#(host `hostname` | cut -d " " -f 4)
# SPOTIFY
unbind s
bind s run-shell '~/.tmux/spotify_tmux.sh'
bind + run-shell '~/.tmux/spotify_tmux.sh n'
bind s run -b '~/.tmux/spotify-tmux.sh >/dev/null'\; display-message "Spotify: Starting"
bind -n C-n run -b '~/.tmux/spotify-tmux.sh n >/dev/null' \; display-message "Spotify: Next"
bind -n C-p run -b '~/.tmux/spotify-tmux.sh p >/dev/null' \; display-message "Spotify: Previous"
bind -n C-Space run -b '~/.tmux/spotify-tmux.sh s >/dev/null' \; display-message "Spotify Play/Pause"
# PLUGIN Management

View File

@ -7,15 +7,25 @@ TARGET_COMMAND='spotify_player'
LIMIT=1000
VOLUME=65
SPOTIFY_PID=0
CHANGE_VOL=0
for ARGUMENT in "$@"
do
KEY=$(echo "$ARGUMENT"| cut -f1 -d=)
KEY_LENGTH=${#KEY}
VALUE="${ARGUMENT:$KEY_LENGTH+1}"
if [[ "$KEY" == "-v" ]]; then
VOLUME=$VALUE
CHANGE_VOL=1
shift #remove the used switch
fi
done
POSITION=$(tmux list-windows | awk -v name="$TARGET_NAME" '$0 ~ name {print $1}' | awk -F: '{print $1}')
echo "setting VOlume"
fi
exit 1
check_deps() {
if $("which $TARGET_COMMAND" 2>/dev/null); then
if $(which tmux 2>/dev/null); then
if command -v "$TARGET_COMMAND" >/dev/null 2>&1; then
if $(command -v tmux >/dev/null 2>&1); then
echo 1
else
echo 0
@ -23,7 +33,7 @@ check_deps() {
fi
}
display() {
echo "$1"
# echo "$1"
tmux display-message "$1"
}
check_running() {
@ -33,7 +43,7 @@ check_running() {
echo 0
else
echo 1
SPOTIFY_PID=$(pidof spotify_player)
SPOTIFY_PID=$(pgrep spotify_player)
fi
}
@ -49,31 +59,34 @@ get_volume() {
set_volume() {
local volume
volume=get_volume
if "$volume" != "$VOLUME"; then
volume=$(get_volume)
if [[ "$volume" != "$VOLUME" ]]; then
$TARGET_COMMAND playback volume $VOLUME
fi
}
check_deps
check_deps > /dev/null
check_running >/dev/null
if [ "$#" -lt 1 ]; then
if [ $SPOTIFY_PID != 0 ]; then
if [[ "$CHANGE_VOL" == 1 ]]; then
set_volume
exit 0
fi
display "Spotify Already Running"
exit 12
fi
if [ -n "$POSITION" ] && [ "$POSITION" -ne 1 ]; then
tmux swap-window -s $TARGET_NAME -t 1
elif
[ -n "$POSITION" ] && [ "$POSITION" -eq 1 ]
elif [ -n "$POSITION" ] && [ "$POSITION" -eq 1 ];
then
tmux select-window -t $TARGET_NAME
else
display "Starting Spotify"
tmux new-window -n $TARGET_NAME $TARGET_COMMAND &&
tmux swap-window -s $TARGET_NAME -t 1
display "Window Ready"
echo check_running
display "Spotify Window Created"
until [ "$(check_running)" = "1" ]; do
display "Waiting for Spotify"
sleep 1
@ -96,22 +109,20 @@ else
display "Spotify Is Not Running"
exit 0
fi
echo "$1"
case "$1" in
n)
$TARGET_COMMAND playback next
"n")
$TARGET_COMMAND playback next && set_volume
;;
p)
$TARGET_COMMAND playback previous
"p")
$TARGET_COMMAND playback previous && set_volume
;;
s)
$TARGET_COMMAND playback play-pause
;;
v)
$TARGET_COMMAND playback volume $VOLUME
"s")
$TARGET_COMMAND playback play-pause && set_volume
;;
*)
echo "Usage: $0 [n|p|s]"
echo "Usage: $0 [-n|-p|-s|-v]"
exit 1
;;
esac

20
vscode/.vscode/argv.json vendored Normal file
View File

@ -0,0 +1,20 @@
// This configuration file allows you to pass permanent command line arguments to VS Code.
// Only a subset of arguments is currently supported to reduce the likelihood of breaking
// the installation.
//
// PLEASE DO NOT CHANGE WITHOUT UNDERSTANDING THE IMPACT
//
// NOTE: Changing this file requires a restart of VS Code.
{
// Use software rendering instead of hardware accelerated rendering.
// This can help in cases where you see rendering issues in VS Code.
// "disable-hardware-acceleration": true,
// Allows to disable crash reporting.
// Should restart the app if the value is changed.
"enable-crash-reporter": true,
// Unique id used for correlating crash reports sent from this instance.
// Do not edit this value.
"crash-reporter-id": "85c4a9e2-2510-427a-a803-2ea801f38920"
}

1
vscode/.vscode/extensions/.obsolete vendored Normal file
View File

@ -0,0 +1 @@
{"ms-python.python-2024.10.0-darwin-arm64":true}

View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: [batisteo]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: batisteo
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="vscode-django" Version="1.15.0" Publisher="batisteo" />
<DisplayName>Django</DisplayName>
<Description xml:space="preserve">Beautiful syntax and scoped snippets for perfectionists with deadlines</Description>
<Tags>python,django,web,snippet,django-html,Django HTML,django-txt,Django txt,__web_extension</Tags>
<Categories>Programming Languages,Snippets</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.49.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace,web" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/vscode-django/vscode-django.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/vscode-django/vscode-django.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/vscode-django/vscode-django.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/vscode-django/vscode-django/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/vscode-django/vscode-django" />
<Property Id="Microsoft.VisualStudio.Services.Branding.Color" Value="#0c4b33" />
<Property Id="Microsoft.VisualStudio.Services.Branding.Theme" Value="dark" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.Content.Pricing" Value="Free"/>
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/images/vscode-django-icon.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/images/vscode-django-icon.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@ -0,0 +1,7 @@
# Change Log
All notable changes to the "django" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2021 vscode-django contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.

View File

@ -0,0 +1,4 @@
all: publish
publish:
vsce publish

View File

@ -0,0 +1,99 @@
# Django extension for Visual Studio Code
> Beautiful syntax and scoped snippets for perfectionists with deadlines
![Syntax with Gruvbox](https://github.com/vscode-django/vscode-django/raw/HEAD/images/vscode-django-syntax-gruvbox.png)
## Features
### Go to definition in templates
Ctrl+click (cmd+click on MacOS) or press F12 on the template path in a `include` or `extends` tag
to jump to this template
### Snippets
- Support for selected text (when inserting snippet from the menu)
- Support for copied text
- No unnecessary new lines
### Improved syntax
- Adds the filetype `django-html`
- Adds the filetype `django-txt` for email templates.
- Better syntax with more operators and default keywords:
- Known default tags and filters
- Known templatetags namespace from contrib in the {% load %} tag
- Known keywords in tags, like: `as`, `asvar`, `with`, `trimmed`
- Syntax highlighting everywhere in your HTML document:
- In the HTML tag itself"
- In the id, class or any attribute
- In inline CSS or Javascript code
## Tricks
### Gettext and internationalization
Dealing with `django.po` files?
Consider installing the [Gettext extension](https://marketplace.visualstudio.com/items?itemName=mrorz.language-gettext) by MrOrz.
### Emmet
Add the following item to the **Emmet: Include Languages** settings:
| Item | Value |
| ------------- | ------ |
| `django-html` | `html` |
## Sponsors
- [tpberntsen](https://github.com/tpberntsen)
- [moving-content](https://github.com/moving-content)
[![Paypal](https://img.shields.io/static/v1?label=Paypal&message=€66&logo=Paypal&color=009cde&link=https://www.paypal.com/paypalme/batisteo/5)](https://www.paypal.com/paypalme/batisteo/5)
[![Github Sponsor](https://img.shields.io/static/v1?label=Sponsor&message=6%C2%A0%E2%9D%A4&logo=GitHub&color=ea4aaa&link=https://github.com/sponsor/batisteo)](https://github.com/sponsor/batisteo)
## Contributing
### Issues
Something odd? New feature request?
Please [create an issue on Github](https://github.com/vscode-django/vscode-django/issues/new).
### Setup
```bash
git clone https://github.com/vscode-django/vscode-django
cd vscode-django
npm install
code .
```
Its better to have [TSlint](https://marketplace.visualstudio.com/items?itemName=eg2.tslint) installed.
### Launching the extension debugger
Make sure you have this snippet in `.vscode/launch.json`:
```javascript
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
]
}
]
}
```
Press <kbd>F5</kbd> or click on Debug then Start (▶️) to launch the extension host window.
Hack around
Press <kbd>Ctrl</kbd> + <kbd>Shift</kbd> + <kbd>F5</kbd> or 🔄 to reload.

View File

@ -0,0 +1,47 @@
[[snippets]]
prefix = "class_modeladmin"
body = """
@admin.register($1)
class $1Admin(admin.ModelAdmin):
$0
"""
detail = "class ModelAdmin(admin.ModelAdmin)"
description = """```python
@admin.register(Model)
class ModelAdmin(admin.ModelAdmin):
```
"""
[[snippets]]
prefix = "class_stackedinline"
body = """
class ${1}Inline(admin.StackedInline):
model = $1
${2|extra,min_num,max_num,can_delete,show_change_link,verbose_name,verbose_name_plural,fk_name,template,formset|}
"""
description = "Stacked Inline"
[[snippets]]
prefix = "class_tabularinline"
body = """
class $1Inline(admin.TabularInline):
model = $1
${2|extra,min_num,max_num,can_delete,show_change_link,verbose_name,verbose_name_plural,fk_name,template,formset|}
"""
description = "Tabular Inline"
[[snippets]]
prefix = "class_simplelistfilter"
body = """
class $1Filter(admin.SimpleListFilter):
title = _("${2:$1}")
parameter_name = "$3"
def lookups(self, request, model_admin):
return ($0)
def queryset(self, request, queryset):
return queryset.filter($3=self.value())
"""
description = "Admin SimpleList Filter"

View File

@ -0,0 +1,18 @@
[[snippets]]
prefix = "import_admin"
body = "from django.contrib import admin"
detail = "from * import admin"
description = """```python
from django.contrib import admin
```
"""
[[snippets]]
prefix = "import_adminsite"
body = "from django.contrib.admin import AdminSite"
detail = "from * import AdminSite"
description = """```python
from django.contrib import AdminSite
```
"""

View File

@ -0,0 +1,47 @@
[[snippets]]
prefix = "admin_param"
body = "${1|list_display,list_display_links,list_filter,list_select_related,list_per_page,list_max_show_all,list_editable,search_fields,date_hierarchy,save_as,save_as_continue,save_on_top,paginator,preserve_filters,inlines,add_form_template,change_form_template,change_list_template,delete_confirmation_template,delete_selected_confirmation_template,object_history_template,popup_response_template,actions,action_form,actions_on_top,actions_on_bottom,actions_selection_counter,checks_class|} = "
detail = "ModelAdmin parameters"
description = """```python
list_display = ("",)
search_fields = ("",)
date_hierarchy = ("",)
```
"""
[[snippets]]
prefix = "inline_param"
body = "${1|extra,min_num,max_num,can_delete,show_change_link,verbose_name,verbose_name_plural,fk_name,template,formset|} = "
detail = "Inline parameters"
description = """```python
extra = 1
min_num = 3
max_num = 20
```
"""
[[snippets]]
prefix = "fieldsets"
body = """
fieldsets = (
(None, {
"fields": (
$1
),
}),
)
"""
detail = "fieldsets = ((None, {}))"
description = """```python
fieldsets = (
(None, {
"fields": (
$1
),
}),
)
```
"""

View File

@ -0,0 +1,21 @@
[[snippets]]
prefix = "Form"
body = """
class $1Form(forms.Form):
$0
"""
detail = "class Form(forms.Form)"
description = ""
[[snippets]]
prefix = "ModelForm"
body = """
class $1Form(forms.ModelForm):
$0
class Meta:
model = $1
fields = ("$2",$3)
"""
detail = "class $1Form(forms.ModelForm)"
description = ""

View File

@ -0,0 +1,50 @@
[[snippets]]
prefix = "simplearray_field"
body = "SimpleArrayField()"
description = "postgres.forms.SimpleArrayField()"
[[snippets]]
prefix = "splitarray_field"
body = "SplitArrayField()"
description = "postgres.forms.SplitArrayField()"
[[snippets]]
prefix = "hstore_field"
body = "HStoreField()"
description = "postgres.forms.HStoreField()"
[[snippets]]
prefix = "json_field"
body = "JSONField()"
description = "postgres.forms.JSONField()"
[[snippets]]
prefix = "intrange_field"
body = "IntegerRangeField()"
description = "postgres.forms.IntegerRangeField()"
[[snippets]]
prefix = "floatrange_field"
body = "FloatRangeField()"
description = "postgres.forms.FloatRangeField()"
[[snippets]]
prefix = "datetimerange_field"
body = "DateTimeRangeField()"
description = "postgres.forms.DateTimeRangeField()"
[[snippets]]
prefix = "daterange_field"
body = "DateRangeField()"
description = "postgres.forms.DateRangeField()"
[[snippets]]
prefix = "range_widget"
body = "RangeWidget(${1:base_widget=})"
detail = "postgres.forms.RangeWidget()"
description = """```python
class RangeWidget(base_widget, attrs=None)
```
"""

View File

@ -0,0 +1,150 @@
[[snippets]]
prefix = "bool_field"
body = "forms.BooleanField($1, required=${2:False})"
detail = "forms.BooleanField()"
[[snippets]]
prefix = "char_field"
body = "forms.CharField($1,${2: max_length=$4,} required=${5:False})"
detail = "forms.CharField()"
[[snippets]]
prefix = "choice_field"
body = "forms.ChoiceField($1, choices=[${2:CHOICES}], required=${4:False})"
detail = "forms.ChoiceField()"
[[snippets]]
prefix = "combo_field"
body = "forms.ComboField($1)"
detail = "forms.ComboField()"
[[snippets]]
prefix = "date_field"
body = "forms.DateField($1, required=${2:False})"
detail = "forms.DateField()"
[[snippets]]
prefix = "datetime_field"
body = "forms.DateTimeField($1, required=${2:False})"
detail = "forms.DateTimeField()"
[[snippets]]
prefix = "decimal_field"
body = "forms.DecimalField($1, required=${2:False})"
detail = "forms.DecimalField()"
[[snippets]]
prefix = "duration_field"
body = "forms.DurationField($1, required=${2:False})"
detail = "forms.DurationField()"
[[snippets]]
prefix = "email_field"
body = "forms.EmailField($1, required=${2:False})"
detail = "forms.EmailField()"
[[snippets]]
prefix = "file_field"
body = "forms.FileField($1,${2: max_length=$4,} required=${5:False})"
detail = "forms.FileField()"
[[snippets]]
prefix = "filepath_field"
body = "forms.FilePathField($1, path=${2:/absolute_path/}, required=${4:False})"
detail = "forms.FilePathField()"
[[snippets]]
prefix = "float_field"
body = "forms.FloatField($1, required=${2:False})"
detail = "forms.FloatField()"
[[snippets]]
prefix = "ip_field"
body = "forms.GenericIPAddressField($1)"
detail = "forms.GenericIPAddressField()"
[[snippets]]
prefix = "img_field"
body = "forms.ImageField($1, required=${2:False})"
detail = "forms.ImageField()"
[[snippets]]
prefix = "int_field"
body = "forms.IntegerField($1, required=${2:False})"
detail = "forms.IntegerField()"
[[snippets]]
prefix = "json_field"
body = "forms.JSONField($1)"
detail = "forms.JSONField()"
[[snippets]]
prefix = "mochoice_field"
body = "forms.ModelChoiceField($1)"
detail = "forms.ModelChoiceField()"
[[snippets]]
prefix = "momuchoice_field"
body = "forms.ModelMultipleChoiceField($1)"
detail = "forms.ModelMultipleChoiceField()"
[[snippets]]
prefix = "muval_field"
body = "forms.MultiValueField($1)"
detail = "forms.MultiValueField()"
[[snippets]]
prefix = "muchoice_field"
body = "forms.MultipleChoiceField($1, choices=[${2:CHOICES}], required=${4:False})"
detail = "forms.MultipleChoiceField()"
[[snippets]]
prefix = "typedmuchoice_field"
body = "forms.TypedMultipleChoiceField($1, choices=[${2:CHOICES}], coerce=${4:TYPE}, required=${5:False})"
detail = "forms.TypedMultipleChoiceField()"
[[snippets]]
prefix = "nullbool_field"
body = "forms.NullBooleanField($1, required=${2:False})"
detail = "forms.NullBooleanField()"
[[snippets]]
prefix = "regex_field"
body = "forms.RegexField($1, regex=${2:REGEX}, required=${4:False})"
detail = "forms.RegexField()"
[[snippets]]
prefix = "slug_field"
body = "forms.SlugField($1, allow_unicode=${2:False}, required=${4:False})"
detail = "forms.SlugField()"
[[snippets]]
prefix = "sdatetime_field"
body = "forms.SplitDateTimeField($1)"
detail = "forms.SplitDateTimeField()"
[[snippets]]
prefix = "time_field"
body = "forms.TimeField($1, required=${2:False})"
detail = "forms.TimeField()"
[[snippets]]
prefix = "tchoice_field"
body = "forms.TypedChoiceField($1, required=${2:False})"
detail = "forms.TypedChoiceField()"
[[snippets]]
prefix = "tmuchoice_field"
body = "forms.TypedMultipleChoiceField($1)"
detail = "forms.TypedMultipleChoiceField()"
[[snippets]]
prefix = "url_field"
body = "forms.URLField($1, required=${2:False})"
detail = "forms.URLField()"
[[snippets]]
prefix = "uuid_field"
body = "forms.UUIDField($1, required=${2:False})"
detail = "forms.UUIDField()"

View File

@ -0,0 +1,17 @@
[[snippets]]
prefix = "from_forms_import"
body = "from .forms import $1"
detail = "from .forms import ModelForm"
description = "Relative import of app form"
[[snippets]]
prefix = "import_forms"
body = "from django import forms"
detail = "from django import forms"
description = "Imports the Django models module"
[[snippets]]
prefix = "import_postgres_fields"
body = "from django.contrib.postgres.forms import ${1|SimpleArrayField,SplitArrayField,HStoreField,JSONField,IntegerRangeField,FloatRangeField,DateTimeRangeField,DateRangeField|}"
description = "PostgreSQL specific forms fields"

View File

@ -0,0 +1,22 @@
[[snippets]]
prefix = "clean_data"
body = """
clean_${1:field}(self):
${2:data} = self.cleaned_data["${1:field}"]
$0
return ${2:data}
"""
detail = ""
description = ""
[[snippets]]
prefix = "clean_data_get"
body = """
clean_${1:field}(self):
${2:data} = self.cleaned_data.get("${1:field}")
$0
return ${2:data}
"""
detail = ""
description = ""

View File

@ -0,0 +1,41 @@
[[snippets]]
prefix = "Model"
detail = "class Model(models.Model)"
body = """
class $1(models.Model):
$0
class Meta:
verbose_name = _("${1/(.+)/${1:/downcase}/}")
verbose_name_plural = _("${1/(.+)/${1:/downcase}/}${2:s}")
def __str__(self):
return self.${3:name}
def get_absolute_url(self):
return reverse("${1/(.+)/${1:/downcase}/}${4:_detail}", kwargs={"${5|pk,slug|}": self.$5})
"""
[[snippets]]
prefix = "modelmixin"
detail = "class Mixin(models.Model)"
body = """
class $1Mixin(models.Model):
$0
class Meta:
abstract = True
"""
description = ""
[[snippets]]
prefix = "queryset"
detail = "class QuerySet(models.QuerySet)"
body = """
class $1QuerySet(models.QuerySet):
pass
"""
description = ""

View File

@ -0,0 +1,64 @@
# https://docs.djangoproject.com/en/stable/ref/contrib/postgres/fields/
[[snippets]]
prefix = 'postgres_array_field'
body = 'ArrayField(${1:base_field=})'
detail = 'ArrayField()'
[[snippets]]
prefix = 'postgres_hstore_field'
body = 'HStoreField($1)'
detail = 'HStoreField()'
[[snippets]]
prefix = 'postgres_ci_char_field'
body = 'CICharField($1)'
detail = 'CICharField()'
description = 'Case-insensitive CharField()'
[[snippets]]
prefix = 'postgres_ci_email_field'
body = 'CIEmailField($1)'
detail = 'CIEmailField()'
description = 'Case-insensitive EmailField()'
[[snippets]]
prefix = 'postgres_ci_text_field'
body = 'CITextField($1)'
detail = 'CITextField()'
description = 'Case-insensitive TextField()'
[[snippets]]
prefix = 'postgres_json_field'
body = 'JSONField(${1:encoder="$2"})'
detail = 'JSONField()'
[[snippets]]
prefix = 'postgres_integer_range_field'
body = 'IntegerRangeField($1)'
detail = "IntegerRangeField()"
description = "Stores a range of integers."
[[snippets]]
prefix = 'postgres_big_integer_range_field'
body = 'BigIntegerRangeField($1)'
detail = "BigIntegerRangeField()"
description = "Stores a range of large integers."
[[snippets]]
prefix = 'postgres_float_range_field'
body = 'FloatRangeField($1)'
detail = "FloatRangeField()"
description = "Stores a range of floating point values."
[[snippets]]
prefix = 'postgres_date_time_range_field'
body = 'DateTimeRangeField($1)'
detail = "DateTimeRangeField()"
description = "Stores a range of timestamps."
[[snippets]]
prefix = 'postgres_date_range_field'
body = 'DateRangeField($1)'
detail = "DateRangeField()"
description = "Stores a range of dates."

View File

@ -0,0 +1,193 @@
# https://docs.djangoproject.com/en/stable/ref/models/fields/
[[snippets]]
prefix = 'auto_field'
body = 'models.AutoField(_("$1"))'
detail = 'models.AutoField()'
description = 'An IntegerField that automatically increments according to available IDs.'
[[snippets]]
prefix = 'big_auto_field'
body = 'models.BigAutoField(_("$1"))'
detail = 'models.BigAutoField()'
description = 'A 64-bit integer, much like an AutoField.'
[[snippets]]
prefix = 'big_integer_field'
body = 'models.BigIntegerField(_("$1"))'
detail = 'models.BigIntegerField()'
description = 'A 64-bit integer, much like an IntegerField.'
[[snippets]]
prefix = 'binary_field'
body = 'models.BinaryField(_("$1"))'
detail = 'models.BinaryField()'
description = 'A field to store raw binary data.'
[[snippets]]
prefix = 'boolean_field'
body = 'models.BooleanField(_("$1"))'
detail = 'models.BooleanField()'
description = 'A true/false field.'
[[snippets]]
prefix = 'char_field'
body = 'models.CharField(_("$1"), max_length=${3:50})'
detail = 'models.CharField()'
description = 'A string field, for small- to large-sized strings.'
[[snippets]]
prefix = 'date_field'
body = 'models.DateField(_("$1"), auto_now=${3:False}, auto_now_add=${4:False})'
detail = 'models.DateField()'
description = 'A date, represented in Python by a datetime.date instance.'
[[snippets]]
prefix = 'date_time_field'
body = 'models.DateTimeField(_("$1"), auto_now=${3:False}, auto_now_add=${4:False})'
detail = 'models.DateTimeField()'
description = 'A date, represented in Python by a datetime.datetime instance.'
[[snippets]]
prefix = 'decimal_field'
body = 'models.DecimalField(_("$1"), max_digits=${3:5}, decimal_places=${4:2})'
detail = 'models.DecimalField()'
description = 'A fixed-precision decimal number, represented in Python by a Decimal instance.'
[[snippets]]
prefix = 'duration_field'
body = 'models.DurationField(_("$1"))'
detail = 'models.DurationField()'
description = 'A field for storing periods of time - modeled in Python by timedelta.'
[[snippets]]
prefix = 'email_field'
body = 'models.EmailField(_("$1"), max_length=${3:254})'
detail = 'models.EmailField()'
description = 'A CharField that checks that the value is a valid email address.'
[[snippets]]
prefix = 'file_field'
body = 'models.FileField(_("$1"), upload_to=${3:None}, max_length=${4:100})'
detail = 'models.FileField()'
description = 'A file-upload field.'
[[snippets]]
prefix = 'file_path_field'
body = 'models.FilePathField(_("$1"), path=${3:None}, match=${4:None}, recursive=${5:recursive}, max_length=${6:100})'
detail = 'models.FilePathField()'
description = 'A CharField whose choices are limited to the filenames in a certain directory on the filesystem.'
[[snippets]]
prefix = 'float_field'
body = 'models.FloatField(_("$1"))'
detail = 'models.FloatField()'
description = 'A floating-point number represented in Python by a float instance.'
[[snippets]]
prefix = 'foreign_key'
body = 'models.ForeignKey("${1:app}.${2:Model}", verbose_name=_(""), on_delete=models.${3|CASCADE,PROTECT,SET_NULL,SET_DEFAULT,SET(),DO_NOTHING|})'
detail = 'models.ForeignKey()'
description = 'ForeignKey (fk).\n\nA many-to-one relationship.\n\non_delete will become a required argument in Django 2.0. In older versions it defaults to CASCADE.\n\n'
[[snippets]]
prefix = 'image_field'
body = 'models.ImageField(_("$1"), upload_to=${3:None}, height_field=${4:None}, width_field=${5:None}, max_length=${5:100})'
detail = 'models.ImageField()'
description = 'Inherits all attributes and methods from FileField, but also validates that the uploaded object is a valid image.'
[[snippets]]
prefix = 'integer_field'
body = 'models.IntegerField(_("$1"))'
detail = 'models.IntegerField()'
description = 'An integer. Values from -2147483648 to 2147483647 are safe in all databases supported by Django.'
[[snippets]]
prefix = 'generic_ip_address_field'
body = 'models.GenericIPAddressField(_("$1"), protocol=${3:"both"}, unpack_ipv4=${4:False})'
detail = 'models.GenericIPAddressField()'
description = 'An IPv4 or IPv6 address, in string format.'
[[snippets]]
prefix = 'json_field'
body = 'models.JSONField(_("$1")${2:, encoder=$3}${4:, decoder=$5})'
detail = 'models.JSONField()'
description = 'A field for storing JSON encoded data.'
[[snippets]]
prefix = 'many_to_many_field'
body = 'models.ManyToManyField("${1:app}.${2:Model}", verbose_name=_(""))'
detail = 'models.ManyToManyField()'
description = 'A many-to-many relationship.'
[[snippets]]
prefix = 'null_boolean_field'
body = 'models.NullBooleanField(_("$1"))'
detail = 'models.NullBooleanField()'
description = 'Like a BooleanField, but allows NULL as one of the options.'
[[snippets]]
prefix = 'one_to_one_field'
body = 'models.OneToOneField("${1:app}.${2:Model}", verbose_name=_(""), on_delete=models.${3|CASCADE,PROTECT,SET_NULL,SET_DEFAULT,SET(),DO_NOTHING|})'
detail = 'models.OneToOneField()'
description = 'A one-to-one relationship.'
[[snippets]]
prefix = 'phone_number_field'
body = 'models.PhoneNumberField(_("$1"))'
detail = 'models.PhoneNumberField()'
description = '*external package: django-phonenumber-field*'
[[snippets]]
prefix = 'positive_integer_field'
body = 'models.PositiveIntegerField(_("$1"))'
detail = 'models.PositiveIntegerField()'
description = 'Like an IntegerField, but must be either positive or zero (0).'
[[snippets]]
prefix = 'positive_small_integer_field'
body = 'models.PositiveSmallIntegerField(_("$1"))'
detail = 'models.PositiveSmallIntegerField()'
description = 'Like a PositiveIntegerField, but only allows values under a certain (database-dependent) point.'
[[snippets]]
prefix = 'slug_field'
body = 'models.SlugField(_("$1"))'
detail = 'models.SlugField()'
description = 'A slug is a short label for something, containing only letters, numbers, underscores or hyphens. Theyre generally used in URLs.'
[[snippets]]
prefix = 'small_integer_field'
body = 'models.SmallIntegerField(_("$1"))'
detail = 'models.SmallIntegerField()'
description = 'Like an IntegerField, but only allows values under a certain (database-dependent) point.'
[[snippets]]
prefix = 'text_field'
body = 'models.TextField(_("$1"))'
detail = 'models.TextField()'
description = 'A large text field.'
[[snippets]]
prefix = 'time_field'
body = 'models.TimeField(_("$1"), auto_now=${4:False}, auto_now_add=${5:False})'
detail = 'models.TimeField()'
description = 'A time, represented in Python by a datetime.time instance.'
[[snippets]]
prefix = 'url_field'
body = 'models.URLField(_("$1"), max_length=${3:200})'
detail = 'models.URLField()'
description = 'A CharField for a URL.'
[[snippets]]
prefix = 'us_state_field'
body = 'models.USStateField(_("$1"))'
detail = 'models.USStateField()'
description = '*external package: django-localflavor*'
[[snippets]]
prefix = 'uuid_field'
body = 'models.UUIDField(_("$1"))'
detail = 'models.UUIDField()'
description = 'A field for storing universally unique identifiers. Uses Pythons UUID class.'

View File

@ -0,0 +1,36 @@
[[snippets]]
prefix = "import_model"
body = "from .models import $1"
detail = "from .models import Model"
description = "Relative import of app model"
[[snippets]]
prefix = "import_manager"
body = "from .managers import $1Manager"
detail = "from .managers import Manager"
description = "Relative import of manager"
[[snippets]]
prefix = "import_queryset"
body = "from .managers import $1QuerySet"
detail = "from .managers import QuerySet"
description = "Relative import of queryset"
[[snippets]]
prefix = "import_db_models"
body = "from django.db import models"
detail = "from django.db import models"
description = "Imports the Django models module"
[[snippets]]
prefix = "import_postgres_models"
body = "from django.contrib.postgres.fields import ${1|ArrayField,JSONField,HStoreField,CICharField,CIEmailField,CITextField,IntegerRangeField,BigIntegerRangeField,FloatRangeField,DateTimeRangeField,DateRangeField|}"
detail = "from django.contrib.postgres.fields import"
description = "PostgreSQL specific model fields"
[[snippets]]
prefix = "import_signals"
body = "from django.db.models.signals import ${1|pre_init,post_init,pre_save,post_save,pre_delete,post_delete,m2m_changed,class_prepared,Management signals,pre_migrate,post_migrate|}"
detail = "from django.db.models.signals import"
description = "Signals for Django Model"

View File

@ -0,0 +1,21 @@
[[snippets]]
prefix = "manager"
detail = "class Manager(models.Manager)"
body = """
class $1Manager(models.Manager):
def get_queryset(self):
return super().get_queryset().${2|filter,exclude,order_by,distinct,reverse|}($3)
"""
description = "Add extra Manager methods"
[[snippets]]
prefix = "queryset_from_manager"
detail = "class Manager(models.Manager)"
body = """
class $1Manager(models.Manager):
def get_queryset(self):
return $1QuerySet(self.model, using=self._db)
}
"""
description = "Modify the initial QuerySet the Manager returns."

View File

@ -0,0 +1,21 @@
[[snippets]]
prefix = "__str__"
body = """
__str__(self):
return self.${3:name}
"""
[[snippets]]
prefix = "get_absolute_url"
body = """
get_absolute_url(self):
return reverse("${1:${2:model}_detail}", kwargs={"${3|pk,slug|}": self.$3})
"""
[[snippets]]
prefix = "receiver"
body = """
@receiver($2, sender=${3:Model})
def $1_${2|post_save,pre_init,post_init,pre_save,pre_delete,post_delete,m2m_changed,class_prepared,pre_migrate,post_migrate|}_receiver(sender, **kwargs):
$0
"""

View File

@ -0,0 +1,169 @@
[[snippets]]
prefix = "run_python_function"
body = """
def $1(apps, schema_editor):
${2:Model} = apps.get_model("${3:${TM_FILEPATH/.*[\\/](\w+)[\\/]migrations.*/$1/g}}", "$2")$0
"""
[[snippets]]
prefix = "create_model"
body = """
migrations.CreateModel(
name='$1',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),$3
],
options={
'verbose_name': '$1',
'verbose_name_plural': '$1s$2',
}
)$0
"""
[[snippets]]
prefix = "delete_model"
body = """
migrations.DeleteModel(
name="$1",
)$0
"""
[[snippets]]
prefix = "rename_model"
body = """
migrations.RenameModel(
old_name=$1,
new_name=$2,
)$0
"""
[[snippets]]
prefix = "alter_model_table"
body = """
migrations.AlterModelTable(
name="$1",
table="$2",
)$0
"""
[[snippets]]
prefix = "alter_unique_together"
body = """
migrations.AlterUniqueTogether(
name="$1",
unique_together=("$2", "$3"),
)$0
"""
[[snippets]]
prefix = "alter_index_together"
body = """
migrations.AlterIndexTogether(
name="$1",
index_together=("$2", "$3"),
)$0
"""
[[snippets]]
prefix = "alter_order_with_respect_to"
body = """
migrations.AlterOrderWithRespectTo(
name="$1",
order_with_respect_to=$2,
)$0
"""
[[snippets]]
prefix = "alter_model_options"
body = """
migrations.AlterModelOptions(
name="$1",
options={$2},
)$0
"""
[[snippets]]
prefix = "alter_model_managers"
body = """
migrations.AlterModelManagers(
name="$1",
managers=$2,
)$0
"""
[[snippets]]
prefix = "add_field"
body = """
migrations.AddField(
model_name="$1",
name="$2",
field=$3,
)$0
"""
[[snippets]]
prefix = "remove_field"
body = """
migrations.RemoveField(
model_name="$1",
name="$2",
)$0
"""
[[snippets]]
prefix = "alter_field"
body = """
migrations.AlterField(
model_name="$1",
name="$2",
field=$3,
)$0
"""
[[snippets]]
prefix = "rename_field"
body = """
migrations.RenameField(
model_name="$1",
old_name="$2",
new_name="$3",
)$0
"""
[[snippets]]
prefix = "add_index"
body = """
migrations.AddIndex(
model_name="$1",
index="$2",
)$0
"""
[[snippets]]
prefix = "remove_index"
body = """
migrations.RemoveIndex(
model_name="$1",
name="$2",
)$0
"""
[[snippets]]
prefix = "run_sql"
body = "migrations.RunSQL($1)"
[[snippets]]
prefix = "run_python"
detail = "migrations.RunPython()"
description = "migrations.RunPython(forward, backward)"
body = "migrations.RunPython($1${2:, ${3:migrations.RunPython.noop}}),"
[[snippets]]
prefix = "separate_database_and_state"
body = """
migrations.SeparateDatabaseAndState(
database_operations=${1:None},
state_operations=${2:None},
)
"""

View File

@ -0,0 +1,25 @@
[[snippets]]
prefix = "from_conf_import_settings"
detail = "from django.conf import settings"
body = "from django.conf import settings"
[[snippets]]
prefix = "from_trans_import_gettext"
detail = "import gettext"
body = "from django.utils.translation import ${1|ugettext_lazy,ugettext,gettext_lazy,gettext|} as _"
[[snippets]]
prefix = "from_trans_import_ugettext_lazy"
detail = "import ugettext_lazy as _"
body = "from django.utils.translation import ugettext_lazy as _"
[[snippets]]
prefix = "from_trans_import_ngettext"
detail = "import ngettext (plural)"
body = "from django.utils.translation import ${1|ungettext,ngettext,npgettext|}"
[[snippets]]
prefix = "from_trans_import_pgettext"
detail = "import pgettext (context)"
body = "from django.utils.translation import ${1|pgettext_lazy,pgettext,npgettext|}"

View File

@ -0,0 +1,23 @@
[[snippets]]
prefix = "args"
detail = "*args"
body = "*args"
[[snippets]]
prefix = "args_kwargs"
detail = "*args, **kwargs"
body = "*args, **kwargs"
[[snippets]]
prefix = "method"
detail = "def method(self, *args, **kwargs):"
description = """```python
def method(self, *args, **kwargs):
return super().method(*args, **kwargs)
```
"""
body = """
$1(self, $2*args, **kwargs):
${3:return} super().$1($2*args, **kwargs)$0
"""

View File

@ -0,0 +1,15 @@
[[snippets]]
prefix = "import_library_register"
detail = "register = template.Library()"
description = """```python
from django import template
register = template.Library()
```
"""
body = """
from django import template
register = template.Library()$0
"""

View File

@ -0,0 +1,39 @@
[[snippets]]
prefix = "register_assignment_tag"
body = """
def get_$1(context):
request = context.get("request")
$1 = ${2:[]}
return ${3:$1}"
"""
[[snippets]]
prefix = "register_filter"
body = """
@register.filter
def $1(value):
return value$2
"""
[[snippets]]
prefix = "register_inclusion_tag"
body = """
@register.inclusion_tag(${1:"$2.html"}, takes_context=True)
def $1(context):
request = context.get("request")
$3
return {
"request": request,
$4
}
"""
[[snippets]]
prefix = "register_simple_tag"
body = """
@register.simple_tag(takes_context=True)
def $1(context):
request = context.get("request")
return ${2:"It Works!"}
"""

View File

@ -0,0 +1,20 @@
[[snippets]]
prefix = "from_urls_import_reverse"
body = "from django.urls import ${1|reverse,reverse_lazy,resolve,get_script_prefix|}"
detail = "import reverse, resolve…"
[[snippets]]
prefix = "from_urls_import_url"
body = "from django.conf.urls import ${1|url,include,static|}"
detail = "import url, include…"
[[snippets]]
prefix = "from_urls_import_handler"
body = "from django.conf.urls import ${1|handler400,handler403,handler404,handler500|}"
detail = "import handler404…"
[[snippets]]
prefix = "from_urls_import_path"
body = "from django.urls import ${1|path,re_path,include,reverse,reverse_lazy,static,register_converter|}"
detail = "import path, path_re…"

View File

@ -0,0 +1,42 @@
[[snippets]]
prefix = "url_stack"
body = """
${1|url,re_path|}(
r"^$2/$",
$3${4:.as_view()},
name="$5"
)
"""
[[snippets]]
prefix = "url_inline"
body = """
${1|url,re_path|}(r"^$2/$", $3${4:.as_view()}, name="$5"),
"""
detail = """url(r"^/$", View.as_view(), name="")"""
[[snippets]]
prefix = "path_stack"
body = """
path(
"$1/",
${2:VIEW}${3:.as_view()},
name="$4"
)
"""
description = "path(route, view, kwargs=None, name=None)"
[[snippets]]
prefix = "path_inline"
body = """path("$1/", $2${3:.as_view()}, name="$4")"""
description = "path(route, view, kwargs=None, name=None)"
[[snippets]]
prefix = "urlpatterns"
body = """
urlpatterns = [
$0
]
"""
detail = "urlpatterns = []"

View File

@ -0,0 +1,30 @@
[[snippets]]
prefix = "regex_pk"
body = '''r"^$1(?P<${2:pk}>\d+)/$"'''
detail = '''r"^(?P<pk>\d+)/$"'''
[[snippets]]
prefix = "regex_slug"
body = '''r"^$1(?P<${2:slug}>[-\w]+)/$"'''
detail = '''r"^(?P<slug>[-\w]+)/$"'''
[[snippets]]
prefix = "path_str"
body = "<str:$1>"
[[snippets]]
prefix = "path_int"
body = "<int:$1>"
[[snippets]]
prefix = "path_slug"
body = "<slug:$1>"
[[snippets]]
prefix = "path_uuid"
body = "<uuid:$1>"
[[snippets]]
prefix = "path_path"
body = "<path:$1>"

View File

@ -0,0 +1,53 @@
[[snippets]]
prefix = "class_createview"
detail = "class ModelCreateView(CreateView)"
body = """
class ${1:Model}CreateView(CreateView):
model = $1
template_name = "$2.html"$0
"""
[[snippets]]
prefix = "class_deleteview"
detail = "class ModelDeleteView(DeleteView)"
body = """
class ${1:Model}DeleteView(DeleteView):
model = $1
template_name = "$2.html"$0
"""
[[snippets]]
prefix = "class_detailview"
detail = "class ModelDetailView(DetailView)"
body = """
class ${1:Model}DetailView(DetailView):
model = $1
template_name = "$2.html"$0
"""
[[snippets]]
prefix = "class_listview"
detail = "class ModelListView(ListView)"
body = """
class ${1:Model}ListView(ListView):
model = $1
template_name = "$2.html"$0
"""
[[snippets]]
prefix = "class_templateview"
detail = "class ModelView(TemplateView)"
body = """
class ${1:Model}View(TemplateView):
template_name = "$2.html"$0
"""
[[snippets]]
prefix = "class_updateview"
detail = "class ModelUpdateView(UpdateView)"
body = """
class ${1:Model}UpdateView(UpdateView):
model = $1
template_name = "$2.html"$0
"""

View File

@ -0,0 +1,10 @@
[[snippets]]
prefix = "from_views_import"
detail = ""
body = "from .views import $1"
[[snippets]]
prefix = "from_generic_import_views"
detail = "Generic class-based views"
body = "from django.views.generic import ${1|CreateView,DetailView,FormView,ListView,TemplateView,UpdateView|}"

View File

@ -0,0 +1,54 @@
[[snippets]]
prefix = "dispatch"
detail = "dispatch(self, request, *args, **kwargs)"
body = """
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
"""
[[snippets]]
prefix = "get_context_data"
detail = "get_context_data(self, **kwargs)"
body = """
def get_context_data(self, **kwargs) -> dict[str, Any]:
context = super().get_context_data(**kwargs)
context["$1"] = $2
return context
"""
[[snippets]]
prefix = "get_queryset"
detail = "get_queryset(self)"
body = """
def get_queryset(self):
${1:return} super().get_queryset()$0
"""
[[snippets]]
prefix = "get_template_names"
detail = "get_template_names(self)"
body = """
def get_template_names(self) -> list[str]:
if ${1:condition}:
return ["$2"]
return super().get_template_names()
"""
[[snippets]]
prefix = "form_valid"
detail = "form_valid(self, form)"
body = """
def form_valid(self, form):
${1:form}
return super().form_valid(form)
"""
[[snippets]]
prefix = "form_invalid"
detail = "form_invalid(self, form)"
body = """
def form_invalid(self, form):
response = super().form_invalid(form)
${1:response}
"""

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
<circle r="128" cy="128" cx="128" id="path37" style="fill:#ffffff" />
<path id="d" style="fill:#44b78b" d="m 108,40 h 32 v 147.6 c -13.6,2.7 -27.4,4.2 -41.3,4.3 C 59.7,191.9 40,174.5 40,141.1 40,109.0 60.9,86.4 94.2,86.4 c 4.6,-0.0 9.2,0.4 13.7,1.6 z m 0.1,73.4 c -3.4,-1.1 -7.1,-1.6 -10.7,-1.6 -16.0,0 -25.4,11.4 -25.4,28.6 0,16.7 8.8,26.0 25.2,26.0 3.6,0 7.1,-0.2 10.8,-0.8 v -52.2 z" />
<path id="j" style="fill:#44b78b" d="m 192,88 v 73.1 c 0,25.1 -1.8,37.2 -7.4,47.7 C 178.9,219.5 169.1,227.5 158,232 l -30,-14 c 10.7,-3.6 20.7,-10.9 26.2,-20.8 C 158.7,188.1 160,177.7 160,150.2 V 88 Z" />
<rect id="dot" style="fill:#44b78b" height="32" width="32" y="40" x="160" />
</svg>

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@ -0,0 +1,63 @@
{
"comments": {
// symbols used for start and end a block comment. Remove this entry if your language does not support block comments
"blockComment": [ "{% comment %}", "{% endcomment %}" ]
},
// symbols used as brackets
"brackets": [
["<!--", "-->"],
["<", ">"],
["{", "}"],
["{{", "}}"],
["{%", "%}"],
["{#", "#}"],
["(", ")"]
],
// symbols that are auto closed when typing
"autoClosingPairs": [
{ "open": "{", "close": "}"},
{ "open": "%", "close": "%", "notIn": [ "comment", "string" ]},
{ "open": "[", "close": "]"},
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'" },
{ "open": "`", "close": "`" },
{ "open": "\"", "close": "\"" },
{ "open": "<!--", "close": "-->", "notIn": [ "comment", "string" ]}
],
// symbols that that can be used to surround a selection
"surroundingPairs": [
{ "open": "'", "close": "'" },
{ "open": "\"", "close": "\"" },
{ "open": "{", "close": "}"},
{ "open": "%", "close": "%"},
{ "open": "[", "close": "]"},
{ "open": "(", "close": ")" },
{ "open": "<", "close": ">" }
],
"folding": {
"markers": {
"start": "^\\s*<!--\\s*#region\\b.*-->",
"end": "^\\s*<!--\\s*#endregion\\b.*-->"
}
},
"wordPattern": "([\\w\\/\\.]+)|(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)",
"onEnterRules": [
{
"beforeText": { "pattern": "<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr))([_:\\w][_:\\w-.\\d]*)(?:(?:[^'\"/>]|\"[^\"]*\"|'[^']*')*?(?!\\/)>)[^<]*$", "flags": "i" },
"afterText": { "pattern": "^<\\/([_:\\w][_:\\w-.\\d]*)\\s*>", "flags": "i" },
"action": {
"indent": "indentOutdent"
}
},
{
"beforeText": { "pattern": "<(?!(?:area|base|br|col|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr))([_:\\w][_:\\w-.\\d]*)(?:(?:[^'\"/>]|\"[^\"]*\"|'[^']*')*?(?!\\/)>)[^<]*$", "flags": "i" },
"action": {
"indent": "indent"
}
}
],
"indentationRules": {
"increaseIndentPattern": "<(?!\\?|(?:area|base|br|col|frame|hr|html|img|input|keygen|link|menuitem|meta|param|source|track|wbr)\\b|[^>]*\\/>)([-_\\.A-Za-z0-9]+)(?=\\s|>)\\b[^>]*>(?!.*<\\/\\1>)|<!--(?!.*-->)|\\{[^}\"']*$",
"decreaseIndentPattern": "^\\s*(<\\/(?!html)[-_\\.A-Za-z0-9]+\\b[^>]*>|-->|\\})"
}
}

View File

@ -0,0 +1,54 @@
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DjangoCompletionItemProvider = void 0;
const vscode_1 = require("vscode");
const constants_1 = require("../constants");
const settings = vscode_1.workspace.getConfiguration("django");
const exclusions = settings.snippets.exclude;
class DjangoCompletionItemProvider {
constructor() {
this.selector = constants_1.PYTHON_SELECTOR;
this.directory = '';
this.files = [];
this.snippets = [];
}
loadSnippets(snippetPrvider) {
return __awaiter(this, void 0, void 0, function* () {
if (!settings.snippets.use)
return;
if (exclusions.some(word => this.directory.includes(word)))
return;
this.snippets = Array.prototype.concat(...yield Promise.all(this.files.filter(file => !exclusions.some(word => file.includes(word)))
.map(file => snippetPrvider.readSnippets(`${this.directory}/${file}`))));
if (!settings.i18n) {
this.snippets = this.snippets.map(snippet => {
snippet.body = snippet.body.replace(/_\("(\S*)"\)/g, '"$1"');
return snippet;
});
}
});
}
buildSnippet(snippet) {
let item = new vscode_1.CompletionItem(snippet.prefix, vscode_1.CompletionItemKind.Snippet);
item.insertText = new vscode_1.SnippetString(snippet.body);
item.detail = snippet.detail;
item.documentation = new vscode_1.MarkdownString(snippet.description);
return item;
}
provideCompletionItems(document, position, token, context) {
return __awaiter(this, void 0, void 0, function* () {
return this.snippets.map(this.buildSnippet);
});
}
}
exports.DjangoCompletionItemProvider = DjangoCompletionItemProvider;
//# sourceMappingURL=base.js.map

View File

@ -0,0 +1,96 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.DjangoUrlCompletionItemProvider = exports.DjangoTemplatetagsCompletionItemProvider = exports.DjangoViewCompletionItemProvider = exports.DjangoModelCompletionItemProvider = exports.DjangoMigrationCompletionItemProvider = exports.DjangoManagerCompletionItemProvider = exports.DjangoFormCompletionItemProvider = exports.DjangoAdminCompletionItemProvider = exports.DjangoPythonCompletionItemProvider = void 0;
const constants_1 = require("../constants");
const base_1 = require("./base");
class DjangoPythonCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = constants_1.PYTHON_SELECTOR;
this.directory = 'python';
this.files = ["imports.toml", "utils.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoPythonCompletionItemProvider = DjangoPythonCompletionItemProvider;
class DjangoAdminCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/admin{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "admin";
this.files = ["classes.toml", "imports.toml", "options.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoAdminCompletionItemProvider = DjangoAdminCompletionItemProvider;
class DjangoFormCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/forms{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "forms";
this.files = ["classes.toml", "imports.toml", "fields.toml", "fields-postgres.toml", "methods.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoFormCompletionItemProvider = DjangoFormCompletionItemProvider;
class DjangoManagerCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/{models,managers,querysets}{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "models";
this.files = ["managers.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoManagerCompletionItemProvider = DjangoManagerCompletionItemProvider;
class DjangoMigrationCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/migrations/**/*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "models";
this.files = ["migrations.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoMigrationCompletionItemProvider = DjangoMigrationCompletionItemProvider;
class DjangoModelCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/{models,migrations}{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "models";
this.files = ["classes.toml", "imports.toml", "fields.toml", "fields-postgres.toml", "methods.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoModelCompletionItemProvider = DjangoModelCompletionItemProvider;
class DjangoViewCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/views{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "views";
this.files = ["classes.toml", "imports.toml", "methods.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoViewCompletionItemProvider = DjangoViewCompletionItemProvider;
class DjangoTemplatetagsCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/templatetags/**/*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "templatetags";
this.files = ["imports.toml", "methods.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoTemplatetagsCompletionItemProvider = DjangoTemplatetagsCompletionItemProvider;
class DjangoUrlCompletionItemProvider extends base_1.DjangoCompletionItemProvider {
constructor(snippetPrvider) {
super();
this.selector = Object.assign({ pattern: '**/urls{**/,}*.py' }, constants_1.PYTHON_SELECTOR);
this.directory = "urls";
this.files = ["imports.toml", "methods.toml", "regexes.toml"];
this.loadSnippets(snippetPrvider);
}
}
exports.DjangoUrlCompletionItemProvider = DjangoUrlCompletionItemProvider;
//# sourceMappingURL=completionItemProvider.js.map

View File

@ -0,0 +1,6 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.PYTHON_SELECTOR = exports.DJANGO_HTML_SELECTOR = void 0;
exports.DJANGO_HTML_SELECTOR = { scheme: 'file', language: 'django-html' };
exports.PYTHON_SELECTOR = { scheme: 'file', language: 'python' };
//# sourceMappingURL=constants.js.map

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,44 @@
'use strict';
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.activate = void 0;
const vscode_1 = require("vscode");
const definitionProvider_1 = require("./providers/definitionProvider");
const completionItemProvider_1 = require("./completions/completionItemProvider");
const utils_1 = require("./utils");
function activate(context) {
return __awaiter(this, void 0, void 0, function* () {
const snippetProvider = new utils_1.SnippetProvider(context.extensionUri);
const definitions = new definitionProvider_1.TemplatePathProvider();
context.subscriptions.push(vscode_1.languages.registerDefinitionProvider(definitions.selector, definitions));
const djangoPythonSnippets = new completionItemProvider_1.DjangoPythonCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoPythonSnippets.selector, djangoPythonSnippets));
const djangoAdminSnippets = new completionItemProvider_1.DjangoAdminCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoAdminSnippets.selector, djangoAdminSnippets));
const djangoFormSnippets = new completionItemProvider_1.DjangoFormCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoFormSnippets.selector, djangoFormSnippets));
const djangoManagerSnippets = new completionItemProvider_1.DjangoManagerCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoManagerSnippets.selector, djangoManagerSnippets));
const djangoMigrationSnippets = new completionItemProvider_1.DjangoMigrationCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoMigrationSnippets.selector, djangoMigrationSnippets));
const djangoModelSnippets = new completionItemProvider_1.DjangoModelCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoModelSnippets.selector, djangoModelSnippets));
const djangoViewSnippets = new completionItemProvider_1.DjangoViewCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoViewSnippets.selector, djangoViewSnippets));
const djangoTemplatetagsSnippets = new completionItemProvider_1.DjangoTemplatetagsCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoTemplatetagsSnippets.selector, djangoTemplatetagsSnippets));
const djangoUrlSnippets = new completionItemProvider_1.DjangoUrlCompletionItemProvider(snippetProvider);
context.subscriptions.push(vscode_1.languages.registerCompletionItemProvider(djangoUrlSnippets.selector, djangoUrlSnippets));
(0, utils_1.postInitHook)();
});
}
exports.activate = activate;
//# sourceMappingURL=extension.js.map

View File

@ -0,0 +1,66 @@
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.TemplatePathProvider = void 0;
const path_1 = require("path");
const vscode_1 = require("vscode");
const constants_1 = require("../constants");
let regex = (regexes) => new RegExp(regexes.map(re => re.source).join(''));
const quote = /(?:\'|\")/;
const path_re = /([\w/\-]+\.[\w]+)/;
const rel_path_re = /((?:(?:\.\/|(?:\.\.\/)+))[\w/\-]+\.[\w]+)/;
const PATH_RE = regex([quote, path_re, quote]);
const RELATIVE_PATH_RE = regex([quote, rel_path_re, quote]);
const BEGIN_OF_FILE = new vscode_1.Position(0, 0);
let cache = {};
class TemplatePathProvider {
constructor() {
this.selector = [constants_1.DJANGO_HTML_SELECTOR, constants_1.PYTHON_SELECTOR];
}
static getTemplate(document, position, token) {
let path;
let search;
let line = document.lineAt(position.line).text;
let match = line.match(PATH_RE);
let relative_match = line.match(RELATIVE_PATH_RE);
if (relative_match) {
path = relative_match[1];
search = vscode_1.workspace.asRelativePath((0, path_1.resolve)((0, path_1.dirname)(document.uri.path), path));
}
else if (match) {
path = match[1];
search = `**/{templates,jinja2}/${path}`;
}
else {
return Promise.resolve(null);
}
let pos = position.character;
let cursorOverPath = pos > line.indexOf(path) && pos < line.indexOf(path) + path.length;
let uri;
if (search in cache) {
uri = Promise.resolve(cache[search]);
}
else {
uri = vscode_1.workspace.findFiles(search, '', 1, token).then(results => {
let result = results.length ? results[0] : null;
if (result)
cache[search] = result;
return result;
});
}
if (cursorOverPath) {
return uri;
}
else {
return Promise.resolve(null);
}
}
provideDefinition(document, position, token) {
return TemplatePathProvider.getTemplate(document, position, token).then(template => {
if (!template)
return null;
return new vscode_1.Location(template, BEGIN_OF_FILE);
});
}
}
exports.TemplatePathProvider = TemplatePathProvider;
//# sourceMappingURL=definitionProvider.js.map

View File

@ -0,0 +1,34 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.postInitHook = exports.SnippetProvider = void 0;
const toml = require("toml");
const vscode = require("vscode");
const util_1 = require("util");
class SnippetProvider {
constructor(extensionUri) {
this.extensionUri = extensionUri;
}
readSnippets(name) {
return __awaiter(this, void 0, void 0, function* () {
const location = vscode.Uri.joinPath(this.extensionUri, 'completions/snippets', name);
const buffer = yield vscode.workspace.fs.readFile(location);
const str = new util_1.TextDecoder("utf-8").decode(buffer);
return toml.parse(str).snippets;
});
}
}
exports.SnippetProvider = SnippetProvider;
function postInitHook() {
return __awaiter(this, void 0, void 0, function* () { });
}
exports.postInitHook = postInitHook;
//# sourceMappingURL=utils.js.map

View File

@ -0,0 +1,191 @@
{
"name": "vscode-django",
"displayName": "Django",
"description": "Beautiful syntax and scoped snippets for perfectionists with deadlines",
"version": "1.15.0",
"publisher": "batisteo",
"license": "MIT",
"icon": "images/vscode-django-icon.png",
"galleryBanner": {
"color": "#0c4b33",
"theme": "dark"
},
"engines": {
"vscode": "^1.49.0"
},
"recommendations": [
"ms-python.python"
],
"keywords": [
"python",
"django",
"web"
],
"categories": [
"Programming Languages",
"Snippets"
],
"homepage": "https://github.com/vscode-django/vscode-django",
"repository": {
"type": "git",
"url": "https://github.com/vscode-django/vscode-django"
},
"bugs": {
"url": "https://github.com/vscode-django/vscode-django/issues"
},
"activationEvents": [
"onLanguage:django-html",
"onLanguage:django-txt",
"onLanguage:python"
],
"main": "./out/extension",
"browser": "./out/extension-web",
"scripts": {
"vscode:prepublish": "yarn run compile",
"compile": "tsc -p ./",
"lint": "eslint src --ext ts",
"watch": "tsc -watch -p ./",
"pretest": "yarn run compile && yarn run lint",
"test": "yarn run compile && node ./node_modules/vscode/bin/test",
"compile-web": "webpack",
"watch-web": "webpack",
"package-web": "webpack --mode production --devtool hidden-source-map",
"chrome": "vscode-test-web --browserType=chromium --extensionDevelopmentPath=. ."
},
"dependencies": {
"path": "^0.12.7",
"toml": "^3"
},
"devDependencies": {
"@types/glob": "^7.1.3",
"@types/mocha": "^8.0.0",
"@types/node": "^12.11.7",
"@types/vscode": "^1.48.0",
"@typescript-eslint/eslint-plugin": "^4.1.1",
"@typescript-eslint/parser": "^4.1.1",
"eslint": "^7.9.0",
"glob": "^7.1.6",
"mocha": "^8.1.3",
"typescript": "^4.0.2",
"vscode-test": "^1.4.0",
"ts-loader": "^9.2.2",
"webpack": "^5.38.1",
"webpack-cli": "^4.7.0",
"@vscode/test-web": "^0.0.11"
},
"contributes": {
"commands": [
{
"command": "extension.Message",
"title": "Message"
}
],
"languages": [
{
"id": "django-html",
"aliases": [
"Django HTML",
"django"
],
"filenamePatterns": [
"**/templates/**/*.html"
],
"firstLine": "{%",
"configuration": "./language-configuration.json"
},
{
"id": "django-txt",
"aliases": [
"Django txt",
"django"
],
"filenamePatterns": [
"**/templates/**/*"
],
"firstLine": "{%",
"configuration": "./language-configuration.json"
}
],
"grammars": [
{
"language": "django-html",
"scopeName": "text.html.django",
"path": "./syntaxes/django-html.tmLanguage.json"
},
{
"language": "django-txt",
"scopeName": "text.django",
"path": "./syntaxes/django-txt.tmLanguage.json"
}
],
"snippets": [
{
"language": "django-html",
"path": "./snippets/templates/tags.json"
},
{
"language": "django-html",
"path": "./snippets/templates/filters.json"
},
{
"language": "django-txt",
"path": "./snippets/templates/tags.json"
},
{
"language": "django-txt",
"path": "./snippets/templates/filters.json"
}
],
"configurationDefaults": {
"[django-html]": {
"editor.quickSuggestions": {
"other": true,
"comments": true,
"strings": true
}
}
},
"configuration": {
"type": "object",
"title": "Django configuration",
"properties": {
"django.snippets.use": {
"type": "boolean",
"default": true,
"description": "Activates the Python snippets"
},
"django.snippets.exclude": {
"type": "array",
"default": [
"cms",
"wagtail"
],
"description": "Exclude Python snippets by their file name. Can be 'import', 'postgres'..."
},
"django.i18n": {
"type": "boolean",
"default": true,
"description": "Activates the i18n features for snippets (eg.: _(\"\"))"
},
"django.showContributeNotification": {
"type": "boolean",
"default": true,
"description": "Seldom show notifications about this extension"
}
}
}
},
"__metadata": {
"id": "4b41a5a8-170e-4156-b2c0-10efb270abbc",
"publisherId": "f87b3a80-eb31-405d-a27d-a2d02be8cc0f",
"publisherDisplayName": "Baptiste Darthenay",
"targetPlatform": "undefined",
"isApplicationScoped": false,
"isPreReleaseVersion": false,
"hasPreReleaseVersion": false,
"installedTimestamp": 1718224747414,
"pinned": false,
"preRelease": false,
"source": "gallery"
}
}

View File

@ -0,0 +1,339 @@
{
"_comment" : "This section is autocomplete for django template filters",
"join": {
"description": "Filter |join:\", \"",
"prefix": "join",
"body": "join:${1:\", \"}$0"
},
"addslashes": {
"description": "Filter |addslashes",
"prefix": "addslashes",
"body": "addslashes"
},
"capfirst": {
"description": "Filter |capfirst",
"prefix": "capfirst",
"body": "capfirst"
},
"escapejs": {
"description": "Filter |escapejs",
"prefix": "escapejs",
"body": "escapejs"
},
"floatformat": {
"description": "Filter |floatformat",
"prefix": "floatformat",
"body": "floatformat"
},
"iriencode": {
"description": "Filter |iriencode",
"prefix": "iriencode",
"body": "iriencode"
},
"linenumbers": {
"description": "Filter |linenumbers",
"prefix": "linenumbers",
"body": "linenumbers"
},
"lower": {
"description": "Filter |lower",
"prefix": "lower",
"body": "lower"
},
"make_list": {
"description": "Filter |make_list",
"prefix": "make_list",
"body": "make_list"
},
"slugify": {
"description": "Filter |slugify",
"prefix": "slugify",
"body": "slugify"
},
"stringformat": {
"description": "Filter |stringformat",
"prefix": "stringformat",
"body": "stringformat"
},
"title": {
"description": "Filter |title",
"prefix": "title",
"body": "title"
},
"truncatechars": {
"description": "Filter |truncatechars",
"prefix": "truncatechars",
"body": "truncatechars"
},
"truncatechars_html": {
"description": "Filter |stuff:', truncatechars_html",
"prefix": "truncatechars_html",
"body": "truncatechars_html"
},
"truncatewords": {
"description": "Filter |truncatewords",
"prefix": "truncatewords",
"body": "truncatewords"
},
"truncatewords_html": {
"description": "Filter |stuff:', truncatewords_html",
"prefix": "truncatewords_html",
"body": "truncatewords_html"
},
"upper": {
"description": "Filter |upper",
"prefix": "upper",
"body": "upper"
},
"urlencode": {
"description": "Filter |urlencode",
"prefix": "urlencode",
"body": "urlencode"
},
"urlize": {
"description": "Filter |urlize",
"prefix": "urlize",
"body": "urlize"
},
"urlizetrunc": {
"description": "Filter |urlizetrunc",
"prefix": "urlizetrunc",
"body": "urlizetrunc"
},
"wordcount": {
"description": "Filter |wordcount",
"prefix": "wordcount",
"body": "wordcount"
},
"wordwrap": {
"description": "Filter |wordwrap:number",
"prefix": "wordwrap",
"body": "wordwrap:$1"
},
"ljust": {
"description": "Filter |ljust",
"prefix": "ljust",
"body": "ljust"
},
"rjust": {
"description": "Filter |rjust",
"prefix": "rjust",
"body": "rjust"
},
"center": {
"description": "Filter |center:\"\"",
"prefix": "center",
"body": "center:\"$1\""
},
"cut": {
"description": "Filter |cut:\"\"",
"prefix": "cut",
"body": "cut:\"$1\""
},
"escape": {
"description": "Filter |escape",
"prefix": "escape",
"body": "escape"
},
"force_escape": {
"description": "Filter |force_escape",
"prefix": "force_escape",
"body": "force_escape"
},
"linebreaks": {
"description": "Filter |linebreaks",
"prefix": "linebreaks",
"body": "linebreaks"
},
"linebreaksbr": {
"description": "Filter |linebreaksbr",
"prefix": "linebreaksbr",
"body": "linebreaksbr"
},
"safe": {
"description": "Filter |safe",
"prefix": "safe",
"body": "safe"
},
"safeseq": {
"description": "Filter |safeseq",
"prefix": "safeseq",
"body": "safeseq"
},
"striptags": {
"description": "Filter |striptags",
"prefix": "striptags",
"body": "striptags"
},
"dictsort": {
"description": "Filter |dictsort",
"prefix": "dictsort",
"body": "dictsort"
},
"dictsortreversed": {
"description": "Filter |stuff:dictsortreversed",
"prefix": "dictsortreversed",
"body": "dictsortreversed"
},
"first": {
"description": "Filter |first",
"prefix": "first",
"body": "first"
},
"last": {
"description": "Filter |last",
"prefix": "last",
"body": "last"
},
"length": {
"description": "Filter |length",
"prefix": "length",
"body": "length"
},
"length_is": {
"description": "Filter |length_is",
"prefix": "length_is",
"body": "length_is"
},
"random": {
"description": "Filter |random",
"prefix": "random",
"body": "random"
},
"slice": {
"description": "Filter |slice",
"prefix": "slice",
"body": "slice"
},
"unordered_list": {
"description": "Filter |unordered_list",
"prefix": "unordered_list",
"body": "unordered_list"
},
"add": {
"description": "Filter |add",
"prefix": "add",
"body": "add"
},
"get_digit": {
"description": "Filter |get_digit",
"prefix": "get_digit",
"body": "get_digit"
},
"date": {
"description": "Filter |date",
"prefix": "date",
"body": "date"
},
"time": {
"description": "Filter |time",
"prefix": "time",
"body": "time"
},
"timesince": {
"description": "Filter |timesince",
"prefix": "timesince",
"body": "timesince"
},
"timeuntil": {
"description": "Filter |timeuntil",
"prefix": "timeuntil",
"body": "timeuntil"
},
"default": {
"description": "Filter |default:\"\"",
"prefix": "default",
"body": "default:\"$1\""
},
"default_if_none": {
"description": "Filter |default_if_none:\"\"",
"prefix": "default_if_none",
"body": "default_if_none:\"$1\""
},
"divisibleby": {
"description": "Filter |divisibleby",
"prefix": "divisibleby",
"body": "divisibleby"
},
"yesno": {
"description": "Filter |yesno",
"prefix": "yesno",
"body": "yesno"
},
"filesizeformat": {
"description": "Filter |filesizeformat",
"prefix": "filesizeformat",
"body": "filesizeformat"
},
"pluralize": {
"description": "Filter |pluralize",
"prefix": "pluralize",
"body": "pluralize"
},
"phone2numeric": {
"description": "Filter |phone2numeric",
"prefix": "phone2numeric",
"body": "phone2numeric"
},
"pprint": {
"description": "Filter |pprint",
"prefix": "pprint",
"body": "pprint"
}
}

View File

@ -0,0 +1,524 @@
{
"_comment" : "This section is autocomplete for django template tags",
"autoescape": {
"description": "{% autoescape %}",
"prefix": "autoescape",
"body": "{% autoescape ${1|off,on|} %}$0{% endautoescape %}"
},
"autoescape_paste": {
"description": "{% autoescape %} (paste)",
"prefix": "autoescape_paste",
"body": [
"{% autoescape ${1|off,on|} %}",
"$0${CLIPBOARD}",
"{% endautoescape %}"
]
},
"endautoescape": {
"description": "{% endautoescape %}",
"prefix": "endautoescape",
"body": "{% endautoescape %}"
},
"block": {
"description": "{% block name %}{% endblock name %}",
"prefix": "block",
"body": "{% block $1 %}$0{% endblock $1 %}"
},
"block_unnamed": {
"description": "{% block name %}{% endblock %}",
"prefix": "block_unnamed",
"body": "{% block $1 %}$0{% endblock %}"
},
"endblock": {
"description": "{% endblock %}",
"prefix": "endblock",
"body": "{% endblock %}"
},
"super": {
"description": "{{ block.super }}",
"prefix": "super",
"body": "{{ block.super }}"
},
"cache": {
"description": "{% cache %}{% endcache %}",
"prefix": "cache",
"body": "{% cache ${1:timeout} ${2:cache_name} ${3:dynamic_var} %}$0{% endcache %}"
},
"cache_selection": {
"description": "{% cache %} (selection) {% endcache %}",
"prefix": "cache_selection",
"body": [
"$0{% cache ${1:timeout} ${2:cache_name} ${3:dynamic_var} %}",
"\t${TM_SELECTED_TEXT}",
"$0{% endcache %}"
]
},
"startcache": {
"description": "{% cache %}",
"prefix": "startcache",
"body": "{% cache ${1:timeout} ${2:cache_name} ${3:dynamic_var} %}$0"
},
"endcache": {
"description": "{% endcache %}",
"prefix": "endcache",
"body": "{% endcache %}"
},
"comment_inline": {
"description": "{% comment %} (inline)",
"prefix": "comm",
"body": "{# $1 #}"
},
"comment": {
"description": "{% comment %}",
"prefix": "comment",
"body": "{% comment \"$1\" %}$0{% endcomment %}"
},
"comment_paste": {
"description": "{% comment %} (paste)",
"prefix": "comment",
"body": "{% comment %}${CLIPBOARD}{% endcomment %}"
},
"comment_selection": {
"description": "{% comment %} (selection)",
"prefix": "comment",
"body": [
"$0{% comment %}",
"\t${TM_SELECTED_TEXT}",
"$0{% endcomment %}"
]
},
"endcomment": {
"description": "{% endcomment %}",
"prefix": "endcomment",
"body": "{% endcomment %}"
},
"csrf_token": {
"description": "{% csrf_token %}",
"prefix": "csrf_token",
"body": "{% csrf_token %}"
},
"cycle": {
"description": "{% cycle %}",
"prefix": "cycle",
"body": "{% cycle $1 %}"
},
"debug": {
"description": "{% debug %}",
"prefix": "debug",
"body": "{% debug %}"
},
"extends": {
"description": "{% extends \"template_name.html\" %}",
"prefix": "extends",
"body": "{% extends \"$1.html\" %}$0"
},
"extends_var": {
"description": "{% extends variable %}",
"prefix": "extends_variable",
"body": "{% extends $1 %}$0"
},
"filter": {
"description": "{% filter filter_name %}",
"prefix": "filter",
"body": "{% filter $1 %}$0{% endfilter %}"
},
"endfilter": {
"description": "{% endfilter %}",
"prefix": "endfilter",
"body": "{% endfilter %}"
},
"firstof": {
"description": "{% firstof %}",
"prefix": "firstof",
"body": "{% firstof $1 as $2 %}"
},
"endfirstof": {
"description": "{% endfirstof %}",
"prefix": "endfirstof",
"body": "{% endfirstof %}"
},
"for": {
"description": "{% for %}",
"prefix": "for",
"body": "{% for $1 in $2 %}$0{% endfor %}"
},
"for_paste": {
"description": "{% for %} (paste)",
"prefix": "for_paste",
"body": "{% for $1 in $2 %}$3${CLIPBOARD}$0{% endfor %}"
},
"endfor": {
"description": "{% endfor %}",
"prefix": "endfor",
"body": "{% endfor %}"
},
"forempty": {
"description": "{% for %}{% empty %}{% endfor %}",
"prefix": "forempty",
"body": "{% for $1 in $2 %}$0{% empty %}$3{% endfor %}"
},
"forempty_paste": {
"description": "{% for %}(paste){% empty %}{% endfor %}",
"prefix": "forempty_paste",
"body": "{% for $1 in $2 %}$3${CLIPBOARD}{% empty %}$0{% endfor %}"
},
"if": {
"description": "{% if condition %}",
"prefix": "if",
"body": "{% if $1 %}$0{% endif %}"
},
"if_paste": {
"description": "{% if condition %} (paste)",
"prefix": "if_paste",
"body": "{% if $1 %}$0${CLIPBOARD}{% endif %}"
},
"ifelse": {
"description": "{% if condition %}{% else %}{% endif %}",
"prefix": "ifelse",
"body": "{% if $1 %}$2{% else %}$3{% endif %}"
},
"ifelse_paste": {
"description": "{% if condition %}(paste){% else %}{% endif %}",
"prefix": "ifelse_paste",
"body": "{% if $1 %}$2${CLIPBOARD}{% else %}$3{% endif %}"
},
"elif": {
"description": "{% elif condition %}",
"prefix": "elif",
"body": "{% elif $1 %}"
},
"else": {
"description": "{% else %}",
"prefix": "else",
"body": "{% else %}"
},
"endif": {
"description": "{% endif %}",
"prefix": "endif",
"body": "{% endif %}"
},
"ifchanged": {
"description": "{% ifchanged %}",
"prefix": "ifchanged",
"body": "{% ifchanged $1 %}$0{% endifchanged %}"
},
"endifchanged": {
"description": "{% endifchanged %}",
"prefix": "endifchanged",
"body": "{% endifchanged %}"
},
"include": {
"description": "{% include %}",
"prefix": "include",
"body": "{% include \"$1\" %}"
},
"include_with": {
"description": "{% include with var=value %}",
"prefix": "include_with",
"body": "{% include with $1=$2 %}"
},
"load": {
"description": "{% load tags %}",
"prefix": "load",
"body": "{% load ${1|static,i18n,cache,l10n,tz,humanize,flatpages|} %}"
},
"lorem": {
"description": "{% lorem %}",
"prefix": "lorem",
"body": "{% lorem ${1|1,2,3,4,5,6,7,8,9|} ${2|b,p,w|} ${3:random }%}"
},
"now": {
"description": "{% now %}",
"prefix": "now",
"body": "{% now \"${1|DATE_FORMAT,DATETIME_FORMAT,SHORT_DATE_FORMAT,SHORT_DATETIME_FORMAT,Y-m-d H:i|}\" %}"
},
"regroup": {
"description": "{% regroup %}",
"prefix": "regroup",
"body": "{% regroup $1 by $2 as ${3:_list} %}"
},
"regroup_example": {
"description": "{% regroup %}",
"prefix": "regroup_example",
"body": [
"{% regroup ${1:object_list} by ${2:category} as ${2:category}_list %}",
"<ul>",
" {% for ${2:category}, ${3:obj}_list in ${2:category}_list %}",
" <li>{{ ${2:category} }}",
" <ul>",
" {% for ${3:obj} in ${3:obj}_list %}",
" <li>{{ ${3:obj} }}</li>",
" {% endfor %}",
" </ul>",
" </li>",
" {% endfor %}",
"</ul>"
]
},
"resetcycle": {
"description": "{% resetcycle %}",
"prefix": "resetcycle",
"body": "{% resetcycle %}"
},
"spaceless": {
"description": "{% spaceless %}",
"prefix": "spaceless",
"body": "{% spaceless %}$0{% endspaceless %}"
},
"spaceless_paste": {
"description": "{% spaceless %} (paste)",
"prefix": "spaceless_paste",
"body": [
"{% spaceless %}",
"${CLIPBOARD}$0",
"{% endspaceless %}"
]
},
"endspaceless": {
"description": "{% endspaceless %}",
"prefix": "endspaceless",
"body": "{% endspaceless %}"
},
"static": {
"prefix": "{% static %}",
"description": "static",
"body": "{% static \"${1:$SELECTION}\" %}"
},
"tag": {
"description": "{% tag %}",
"prefix": "tag",
"body": "{% $1 %}"
},
"templatetag": {
"description": "{% templatetag %}",
"prefix": "templatetag",
"body": "{% templatetag ${1|openblock,closeblock,openvariable,closevariable,openbrace,closebrace,opencomment,closecomment|} %}"
},
"trans": {
"description": "{% trans \"text to translate\" %}",
"prefix": "trans",
"body": "{% trans \"${0:$SELECTION}\" %}"
},
"trans_paste": {
"description": "{% trans \"(paste)\" %}",
"prefix": "trans_paste",
"body": "{% trans \"${CLIPBOARD}\" %}"
},
"blocktrans": {
"description": "{% blocktrans %}text to translate{% endblocktrans %}",
"prefix": "blocktrans",
"body": "{% blocktrans %}$0{% endblocktrans %}"
},
"blocktrans_paste": {
"description": "{% blocktrans %}(paste){% endblocktrans %}",
"prefix": "blocktrans_paste",
"body": [
"{% blocktrans ${2:trimmed }%}",
"$0${CLIPBOARD}",
"{% endblocktrans %}"
]
},
"blocktrans_with": {
"description": "{% blocktrans with %}text to translate{% endblocktrans %}",
"prefix": "blocktrans_with",
"body": "{% blocktrans with $1=$2 ${3:trimmed }%}$0{% endblocktrans %}"
},
"blocktrans_with_paste": {
"description": "{% blocktrans with %}(paste){% endblocktrans %}",
"prefix": "blocktrans_with_paste",
"body": ["{% blocktrans with $1=$2 ${3:trimmed }%}",
"$0${CLIPBOARD}",
"{% endblocktrans %}"
]
},
"translate": {
"description": "{% translate \"text to translate\" %}",
"prefix": "translate",
"body": "{% translate \"${0:$SELECTION}\" %}"
},
"translate_paste": {
"description": "{% translate \"(paste)\" %}",
"prefix": "translate_paste",
"body": "{% translate \"${CLIPBOARD}\" %}"
},
"blocktranslate": {
"description": "{% blocktranslate %}text to translate{% endblocktranslate %}",
"prefix": "blocktranslate",
"body": "{% blocktranslate %}$0{% endblocktranslate %}"
},
"blocktranslate_paste": {
"description": "{% blocktranslate %}(paste){% endblocktranslate %}",
"prefix": "blocktranslate_paste",
"body": [
"{% blocktranslate ${2:trimmed }%}",
"$0${CLIPBOARD}",
"{% endblocktranslate %}"
]
},
"blocktranslate_with": {
"description": "{% blocktranslate with %}text to translate{% endblocktranslate %}",
"prefix": "blocktranslate_with",
"body": "{% blocktranslate with $1=$2 ${3:trimmed }%}$0{% endblocktranslate %}"
},
"blocktranslate_with_paste": {
"description": "{% blocktranslate with %}(paste){% endblocktranslate %}",
"prefix": "blocktranslate_with_paste",
"body": ["{% blocktranslate with $1=$2 ${3:trimmed }%}",
"$0${CLIPBOARD}",
"{% endblocktranslate %}"
]
},
"url": {
"description": "{% url \"some-url-name\" %}",
"prefix": "url",
"body": "{% url \"$1\" %}"
},
"urlpk": {
"description": "{% url \"some-url-name\" pk=object.pk %}",
"prefix": "urlpk",
"body": "{% url \"$1\" pk=${2:${3:object}.pk} %}"
},
"urlslug": {
"description": "{% url \"some-url-name\" slug=object.slug %}",
"prefix": "urlslug",
"body": "{% url \"$1\" slug=${2:${3:object}.slug} %}"
},
"urlvar": {
"description": "{% url \"some-url-name\" as the_url %}",
"prefix": "urlvar",
"body": "{% url \"$1\" as $2 %}"
},
"variable": {
"description": "{{ variable }}",
"prefix": "variable",
"body": "{{ $1 }}"
},
"variable_default": {
"description": "{{ variable|default:\"default value\" }}",
"prefix": "variable_default",
"body": "{{ $1|default:\"$2\" }}"
},
"verbatim": {
"description": "{% verbatim name %}some content{% endverbatim name %}",
"prefix": "verbatim",
"body": "{% verbatim $1 %}$0{% endverbatim $1 %}"
},
"verbatim_paste": {
"description": "{% verbatim %} (paste)",
"prefix": "verbatim_paste",
"body": "{% verbatim $1 %}$0${CLIPBOARD}{% endverbatim $1 %}"
},
"endverbatim": {
"description": "{% endverbatim %}",
"prefix": "endverbatim",
"body": "{% endverbatim $1%}"
},
"widthratio": {
"description": "{% widthratio %}",
"prefix": "widthratio",
"body": "{% widthratio ${1:this_value} ${2:max_value} ${3:max_width} ${4:as ${5:width}}%}"
},
"with": {
"description": "{% with key=value %}",
"prefix": "with",
"body": "{% with $1=$2 %}$0{% endwith %}"
},
"endwith": {
"description": "{% endwith %}",
"prefix": "endwith",
"body": "{% endwith %}"
},
"with_selection": {
"description": "{% with key=value %} (selection)",
"prefix": "with_selection",
"body": [
"$0{% with $1=$2 %}",
"\t${TM_SELECTED_TEXT}",
"$0{% endwith %}"
]
},
"with_paste": {
"description": "{% with key=value %} (paste)",
"prefix": "with_paste",
"body": "{% with $1=$2 %}${CLIPBOARD}{% endwith %}"
}
}

View File

@ -0,0 +1,35 @@
import json
from os.path import abspath, dirname, join
from datetime import datetime
import tomlkit
SYNTAX_DIR = abspath(dirname(join("syntaxes", __name__)))
SYNTAXES = ["django-html", "django-txt"]
def load(path):
with open(path) as f:
return tomlkit.loads(f.read())
tomlkit.load = load
def build_file(syntax):
data = tomlkit.load(join(SYNTAX_DIR, f"{syntax}.toml"))
data.add("_comment", "Generated by: poetry run syntax")
for name in data["repositories"]:
repo = tomlkit.load(join(SYNTAX_DIR, "repositories", f"{name}.toml"))
data["repository"][name] = repo
data.remove("repositories")
with open(f"syntaxes/{syntax}.tmLanguage.json", "w") as f:
json.dump(data, f, indent=2)
def main():
for syntax in SYNTAXES:
build_file(syntax)

View File

@ -0,0 +1,701 @@
{
"name": "Django HTML",
"scopeName": "text.html.django",
"fileTypes": [
"html"
],
"_comment": "Generated by: poetry run syntax",
"repository": {
"django-stuff": {
"patterns": [
{
"begin": "{%\\s*comment ?(\"([\\w\\s])*\" )?\\s*%}",
"end": "{%\\s*endcomment\\s*%}",
"name": "comment.block.django"
},
{
"match": "{#.*#}",
"name": "comment.line.number-sign.django"
},
{
"begin": "{{",
"end": "}}",
"name": "storage.type.variable.django",
"patterns": [
{
"include": "#django-template-filter"
}
],
"captures": {
"0": {
"name": "entity.tag.tagbraces.django"
}
}
},
{
"begin": "({%)\\s*(load) ",
"end": "(%})",
"name": "storage.type.templatetag.django",
"patterns": [
{
"include": "#django-template-tag-contrib"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "keyword.control.tag-name.django"
}
}
},
{
"begin": "({%)\\s*(autoescape|endautoescape|block|endblock|blocktrans|endblocktrans|trans|plural|comment|endcomment|debug|extends|filter|firstof|for|empty|endfor|if|elif|else|endif|include|ifchanged|endifchanged|ifequal|endifequal|ifnotequal|endifnotequal|from|low|regroup|ssi|spaceless|endspaceless|templatetag|widthratio|with|endwith|csrf_token|cycle|url|verbatim|endverbatim|lorem|thumbnail|endthumbnail|get_static_prefix)\\b",
"end": "(%})",
"name": "storage.type.templatetag.django",
"patterns": [
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
},
{
"include": "#django-template-tag"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "keyword.control.tag-name.django"
}
}
},
{
"begin": "({%)\\s*([a-zA-Z0-9_.]+)",
"end": "(%})",
"name": "storage.type.customtemplatetag.django",
"patterns": [
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
},
{
"include": "#django-template-tag"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "constant.other.tag.name.django"
}
}
}
]
},
"django-template-filter": {
"patterns": [
{
"match": "(\\|) ?(add|addslashes|capfirst|center|cut|date|default|default_if_none|dictsort|dictsortreversed|divisibleby|escape|escapejs|filesizeformat|first|fix_ampersands|floatformat|force_escape|get_digit|iriencode|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|ljust|lower|make_list|phone2numeric|pluralize|pprint|random|removetags|rjust|safe|safeseq|slice|slugify|stringformat|striptags|time|timesince|timeutil|title|truncatewords|truncatewords_html|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|yesno|apnumber|intcomma|intword|naturalday|ordinal|STATIC_PREFIX)\\b",
"name": "entity.name.function.filter.django",
"captures": {
"1": {
"name": "entity.tag.filter-pipe.django"
},
"2": {
"name": "keyword.control.filter.django"
}
}
},
{
"match": "(\\|)([a-zA-Z0-9_]+)\\b",
"name": "entity.name.function.filter.django",
"captures": {
"1": {
"name": "entity.tag.filter-pipe.django"
},
"2": {
"name": "constant.other.filter.django"
}
}
},
{
"match": ":",
"name": "keyword.operator.filter-argument.django"
},
{
"match": "=",
"name": "keyword.operator.assignment.django"
},
{
"match": ",",
"name": "keyword.operator.argument-separator.django"
},
{
"match": "\\.",
"name": "keyword.operator.getter.django"
},
{
"match": "[a-zA-Z0-9_]+",
"name": "string.unquoted.tag-string.django"
},
{
"match": "(>=|>|==|!=|<|<=|and|or|is|not|in)",
"name": "keyword.operator.condition.django"
},
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
}
]
},
"django-template-tag-contrib": {
"patterns": [
{
"match": "\\b(cache|i18n|l10n|static|tz|flatpages|humanize|admin_list|admin_modify|admin_static|admin_urls|base|log)\\b",
"name": "constant.other.contrib.django"
}
]
},
"django-template-tag": {
"patterns": [
{
"match": "\\b(and|or|not|is|in|by|as|asvar|trimmed|with)\\b",
"name": "keyword.operator.django"
}
]
},
"entities": {
"patterns": [
{
"match": "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)",
"name": "constant.character.entity.html",
"captures": {
"1": {
"name": "punctuation.definition.entity.html"
},
"3": {
"name": "punctuation.definition.entity.html"
}
}
},
{
"match": "&",
"name": "invalid.illegal.bad-ampersand.html"
}
]
},
"string-double-quoted": {
"begin": "\"",
"end": "\"",
"name": "string.quoted.double.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
},
"string-single-quoted": {
"begin": "'",
"end": "'",
"name": "string.quoted.single.html",
"patterns": [
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
},
"tag-generic-attribute": {
"match": "\\b([a-zA-Z\\-:]+)",
"name": "entity.other.attribute-name.html"
},
"tag-id-attribute": {
"begin": "\\b(id)\\b\\s*(=)",
"end": "(?<='|\")",
"name": "meta.attribute-with-value.id.html",
"patterns": [
{
"begin": "\"",
"contentName": "meta.toc-list.id.html",
"end": "\"",
"name": "string.quoted.double.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
},
{
"begin": "'",
"contentName": "meta.toc-list.id.html",
"end": "'",
"name": "string.quoted.single.html",
"patterns": [
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
}
],
"captures": {
"1": {
"name": "entity.other.attribute-name.id.html"
},
"2": {
"name": "punctuation.separator.key-value.html"
}
}
},
"tag-stuff": {
"patterns": [
{
"include": "#tag-id-attribute"
},
{
"include": "#tag-generic-attribute"
},
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
}
]
}
},
"patterns": [
{
"include": "#django-stuff"
},
{
"begin": "(<)([a-zA-Z0-9:]++)(?=[^>]*></\\2>)",
"end": "(>)(<)(/)(\\2)(>)",
"name": "meta.tag.any.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#tag-stuff"
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.html"
}
},
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
},
"2": {
"name": "punctuation.definition.tag.begin.html meta.scope.between-tag-pair.html"
},
"3": {
"name": "punctuation.definition.tag.begin.html"
},
"4": {
"name": "entity.name.tag.html"
},
"5": {
"name": "punctuation.definition.tag.end.html"
}
}
},
{
"begin": "(<\\?)(xml)",
"end": "(\\?>)",
"name": "meta.tag.preprocessor.xml.html",
"patterns": [
{
"include": "#tag-generic-attribute"
},
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
}
],
"captures": {
"1": {
"name": "punctuation.definition.tag.html"
},
"2": {
"name": "entity.name.tag.xml.html"
}
}
},
{
"begin": "<!--",
"end": "--\\s*>",
"name": "comment.block.html",
"patterns": [
{
"match": "--",
"name": "invalid.illegal.bad-comments-or-CDATA.html"
}
],
"captures": {
"0": {
"name": "punctuation.definition.comment.html"
}
}
},
{
"begin": "<!",
"end": ">",
"name": "meta.tag.sgml.html",
"patterns": [
{
"begin": "(?i:DOCTYPE)",
"end": "(?=>)",
"name": "meta.tag.sgml.doctype.html",
"patterns": [
{
"match": "\"[^\">]*\"",
"name": "string.quoted.double.doctype.identifiers-and-DTDs.html"
}
],
"captures": {
"1": {
"name": "entity.name.tag.doctype.html"
}
}
},
{
"begin": "\\[CDATA\\[",
"end": "]](?=>)",
"name": "constant.other.inline-data.html"
},
{
"match": "(\\s*)(?!--|>)\\S(\\s*)",
"name": "invalid.illegal.bad-comments-or-CDATA.html"
}
],
"captures": {
"0": {
"name": "punctuation.definition.tag.html"
}
}
},
{
"begin": "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*/>)",
"end": "(</)((?i:style))(>)(?:\\s*\\n)?",
"name": "source.css.embedded.html",
"patterns": [
{
"include": "#tag-stuff"
},
{
"begin": "(>)",
"end": "(?=</(?i:style))",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "source.css.django"
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
}
}
}
],
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.style.html"
},
"3": {
"name": "punctuation.definition.tag.html"
}
}
},
{
"begin": "(?:^\\s+)?(<)((?i:script))\\b(?![^>]*/>)(?!.*type=[\"']text/template['\"])",
"end": "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?",
"name": "source.js.embedded.html",
"patterns": [
{
"include": "#tag-stuff"
},
{
"include": "#django-stuff"
},
{
"begin": "(?<!</(?:script|SCRIPT))(>)",
"end": "(</)((?i:script))",
"patterns": [
{
"begin": "{%\\s*comment\\s*%}",
"end": "{%\\s*endcomment\\s*%}",
"name": "comment.block.django"
},
{
"match": "{#.*#}",
"name": "comment.line.number-sign.django"
},
{
"begin": "{{",
"end": "}}",
"name": "storage.type.variable.django",
"patterns": [
{
"include": "#django-template-filter"
}
],
"captures": {
"0": {
"name": "entity.tag.tagbraces.django"
}
}
},
{
"begin": "{%",
"end": "%}",
"name": "storage.type.templatetag.django",
"patterns": [
{
"include": "#django-template-tag"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"0": {
"name": "entity.tag.tagbraces.django"
}
}
},
{
"match": "(//).*?((?=</script)|$\\n?)",
"name": "comment.line.double-slash.js",
"captures": {
"1": {
"name": "punctuation.definition.comment.js"
}
}
},
{
"begin": "/\\*",
"end": "\\*/|(?=</script)",
"name": "comment.block.js",
"captures": {
"0": {
"name": "punctuation.definition.comment.js"
}
}
},
{
"include": "source.js"
}
],
"captures": {
"1": {
"name": "punctuation.definition.tag.end.html"
},
"2": {
"name": "entity.name.tag.script.html"
}
}
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.script.html"
}
},
"endCaptures": {
"2": {
"name": "punctuation.definition.tag.html"
}
}
},
{
"begin": "(</?)((?i:body|head|html)\\b)",
"end": "(>)",
"name": "meta.tag.structure.any.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#tag-stuff"
}
],
"captures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.structure.any.html"
}
},
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
}
}
},
{
"begin": "(</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)",
"end": "(>)",
"name": "meta.tag.block.any.html",
"patterns": [
{
"include": "#tag-stuff"
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.block.any.html"
}
},
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
}
}
},
{
"begin": "(</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)",
"end": "((?: ?/)?>)",
"name": "meta.tag.inline.any.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#tag-stuff"
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.inline.any.html"
}
},
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
}
}
},
{
"begin": "(</?)([a-zA-Z0-9:]+)",
"end": "(>)",
"name": "meta.tag.other.html",
"patterns": [
{
"include": "#tag-stuff"
}
],
"beginCaptures": {
"1": {
"name": "punctuation.definition.tag.begin.html"
},
"2": {
"name": "entity.name.tag.other.html"
}
},
"endCaptures": {
"1": {
"name": "punctuation.definition.tag.end.html"
}
}
},
{
"include": "#entities"
},
{
"match": "<>",
"name": "invalid.illegal.incomplete.html"
},
{
"match": "<",
"name": "invalid.illegal.bad-angle-bracket.html"
}
]
}

View File

@ -0,0 +1,271 @@
name = "Django HTML"
scopeName = "text.html.django"
fileTypes = ["html"]
repositories = [
"django-stuff",
"django-template-filter",
"django-template-tag-contrib",
"django-template-tag",
"entities",
"string-double-quoted",
"string-single-quoted",
"tag-generic-attribute",
"tag-id-attribute",
"tag-stuff",
]
[repository]
[[patterns]]
include = "#django-stuff"
[[patterns]]
begin = "(<)([a-zA-Z0-9:]++)(?=[^>]*></\\2>)"
end = "(>)(<)(/)(\\2)(>)"
name = "meta.tag.any.html"
[[patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns]]
include = "#tag-stuff"
[patterns.beginCaptures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.beginCaptures.2]
name = "entity.name.tag.html"
[patterns.endCaptures.1]
name = "punctuation.definition.tag.end.html"
[patterns.endCaptures.2]
name = "punctuation.definition.tag.begin.html meta.scope.between-tag-pair.html"
[patterns.endCaptures.3]
name = "punctuation.definition.tag.begin.html"
[patterns.endCaptures.4]
name = "entity.name.tag.html"
[patterns.endCaptures.5]
name = "punctuation.definition.tag.end.html"
[[patterns]]
begin = "(<\\?)(xml)"
end = "(\\?>)"
name = "meta.tag.preprocessor.xml.html"
[[patterns.patterns]]
include = "#tag-generic-attribute"
[[patterns.patterns]]
include = "#string-double-quoted"
[[patterns.patterns]]
include = "#string-single-quoted"
[patterns.captures.1]
name = "punctuation.definition.tag.html"
[patterns.captures.2]
name = "entity.name.tag.xml.html"
[[patterns]]
begin = "<!--"
end = "--\\s*>"
name = "comment.block.html"
[[patterns.patterns]]
match = "--"
name = "invalid.illegal.bad-comments-or-CDATA.html"
[patterns.captures.0]
name = "punctuation.definition.comment.html"
[[patterns]]
begin = "<!"
end = ">"
name = "meta.tag.sgml.html"
[[patterns.patterns]]
begin = "(?i:DOCTYPE)"
end = "(?=>)"
name = "meta.tag.sgml.doctype.html"
[[patterns.patterns.patterns]]
match = "\"[^\">]*\""
name = "string.quoted.double.doctype.identifiers-and-DTDs.html"
[patterns.patterns.captures.1]
name = "entity.name.tag.doctype.html"
[[patterns.patterns]]
begin = "\\[CDATA\\["
end = "]](?=>)"
name = "constant.other.inline-data.html"
[[patterns.patterns]]
match = "(\\s*)(?!--|>)\\S(\\s*)"
name = "invalid.illegal.bad-comments-or-CDATA.html"
[patterns.captures.0]
name = "punctuation.definition.tag.html"
[[patterns]]
begin = "(?:^\\s+)?(<)((?i:style))\\b(?![^>]*/>)"
end = "(</)((?i:style))(>)(?:\\s*\\n)?"
name = "source.css.embedded.html"
[[patterns.patterns]]
include = "#tag-stuff"
[[patterns.patterns]]
begin = "(>)"
end = "(?=</(?i:style))"
[[patterns.patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns.patterns]]
include = "source.css.django"
[patterns.patterns.beginCaptures.1]
name = "punctuation.definition.tag.end.html"
[patterns.captures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.captures.2]
name = "entity.name.tag.style.html"
[patterns.captures.3]
name = "punctuation.definition.tag.html"
[[patterns]]
begin = "(?:^\\s+)?(<)((?i:script))\\b(?![^>]*/>)(?!.*type=[\"']text/template['\"])"
end = "(?<=</(script|SCRIPT))(>)(?:\\s*\\n)?"
name = "source.js.embedded.html"
[[patterns.patterns]]
include = "#tag-stuff"
[[patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns]]
begin = "(?<!</(?:script|SCRIPT))(>)"
end = "(</)((?i:script))"
[[patterns.patterns.patterns]]
begin = "{%\\s*comment\\s*%}"
end = "{%\\s*endcomment\\s*%}"
name = "comment.block.django"
[[patterns.patterns.patterns]]
match = "{#.*#}"
name = "comment.line.number-sign.django"
[[patterns.patterns.patterns]]
begin = "{{"
end = "}}"
name = "storage.type.variable.django"
[[patterns.patterns.patterns.patterns]]
include = "#django-template-filter"
[patterns.patterns.patterns.captures.0]
name = "entity.tag.tagbraces.django"
[[patterns.patterns.patterns]]
begin = "{%"
end = "%}"
name = "storage.type.templatetag.django"
[[patterns.patterns.patterns.patterns]]
include = "#django-template-tag"
[[patterns.patterns.patterns.patterns]]
include = "#django-template-filter"
[patterns.patterns.patterns.captures.0]
name = "entity.tag.tagbraces.django"
[[patterns.patterns.patterns]]
match = "(//).*?((?=</script)|$\\n?)"
name = "comment.line.double-slash.js"
[patterns.patterns.patterns.captures.1]
name = "punctuation.definition.comment.js"
[[patterns.patterns.patterns]]
begin = "/\\*"
end = "\\*/|(?=</script)"
name = "comment.block.js"
[patterns.patterns.patterns.captures.0]
name = "punctuation.definition.comment.js"
[[patterns.patterns.patterns]]
include = "source.js"
[patterns.patterns.captures.1]
name = "punctuation.definition.tag.end.html"
[patterns.patterns.captures.2]
name = "entity.name.tag.script.html"
[patterns.beginCaptures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.beginCaptures.2]
name = "entity.name.tag.script.html"
[patterns.endCaptures.2]
name = "punctuation.definition.tag.html"
[[patterns]]
begin = "(</?)((?i:body|head|html)\\b)"
end = "(>)"
name = "meta.tag.structure.any.html"
[[patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns]]
include = "#tag-stuff"
[patterns.captures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.captures.2]
name = "entity.name.tag.structure.any.html"
[patterns.endCaptures.1]
name = "punctuation.definition.tag.end.html"
[[patterns]]
begin = "(</?)((?i:address|blockquote|dd|div|dl|dt|fieldset|form|frame|frameset|h1|h2|h3|h4|h5|h6|iframe|noframes|object|ol|p|ul|applet|center|dir|hr|menu|pre)\\b)"
end = "(>)"
name = "meta.tag.block.any.html"
[[patterns.patterns]]
include = "#tag-stuff"
[patterns.beginCaptures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.beginCaptures.2]
name = "entity.name.tag.block.any.html"
[patterns.endCaptures.1]
name = "punctuation.definition.tag.end.html"
[[patterns]]
begin = "(</?)((?i:a|abbr|acronym|area|b|base|basefont|bdo|big|br|button|caption|cite|code|col|colgroup|del|dfn|em|font|head|html|i|img|input|ins|isindex|kbd|label|legend|li|link|map|meta|noscript|optgroup|option|param|q|s|samp|script|select|small|span|strike|strong|style|sub|sup|table|tbody|td|textarea|tfoot|th|thead|title|tr|tt|u|var)\\b)"
end = "((?: ?/)?>)"
name = "meta.tag.inline.any.html"
[[patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns]]
include = "#tag-stuff"
[patterns.beginCaptures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.beginCaptures.2]
name = "entity.name.tag.inline.any.html"
[patterns.endCaptures.1]
name = "punctuation.definition.tag.end.html"
[[patterns]]
begin = "(</?)([a-zA-Z0-9:]+)"
end = "(>)"
name = "meta.tag.other.html"
[[patterns.patterns]]
include = "#tag-stuff"
[patterns.beginCaptures.1]
name = "punctuation.definition.tag.begin.html"
[patterns.beginCaptures.2]
name = "entity.name.tag.other.html"
[patterns.endCaptures.1]
name = "punctuation.definition.tag.end.html"
[[patterns]]
include = "#entities"
[[patterns]]
match = "<>"
name = "invalid.illegal.incomplete.html"
[[patterns]]
match = "<"
name = "invalid.illegal.bad-angle-bracket.html"

View File

@ -0,0 +1,255 @@
{
"name": "Django txt",
"scopeName": "text.django",
"fileTypes": [
"txt"
],
"_comment": "Generated by: poetry run syntax",
"repository": {
"django-stuff": {
"patterns": [
{
"begin": "{%\\s*comment ?(\"([\\w\\s])*\" )?\\s*%}",
"end": "{%\\s*endcomment\\s*%}",
"name": "comment.block.django"
},
{
"match": "{#.*#}",
"name": "comment.line.number-sign.django"
},
{
"begin": "{{",
"end": "}}",
"name": "storage.type.variable.django",
"patterns": [
{
"include": "#django-template-filter"
}
],
"captures": {
"0": {
"name": "entity.tag.tagbraces.django"
}
}
},
{
"begin": "({%)\\s*(load) ",
"end": "(%})",
"name": "storage.type.templatetag.django",
"patterns": [
{
"include": "#django-template-tag-contrib"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "keyword.control.tag-name.django"
}
}
},
{
"begin": "({%)\\s*(autoescape|endautoescape|block|endblock|blocktrans|endblocktrans|trans|plural|comment|endcomment|debug|extends|filter|firstof|for|empty|endfor|if|elif|else|endif|include|ifchanged|endifchanged|ifequal|endifequal|ifnotequal|endifnotequal|from|low|regroup|ssi|spaceless|endspaceless|templatetag|widthratio|with|endwith|csrf_token|cycle|url|verbatim|endverbatim|lorem|thumbnail|endthumbnail|get_static_prefix)\\b",
"end": "(%})",
"name": "storage.type.templatetag.django",
"patterns": [
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
},
{
"include": "#django-template-tag"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "keyword.control.tag-name.django"
}
}
},
{
"begin": "({%)\\s*([a-zA-Z0-9_.]+)",
"end": "(%})",
"name": "storage.type.customtemplatetag.django",
"patterns": [
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
},
{
"include": "#django-template-tag"
},
{
"include": "#django-template-filter"
}
],
"captures": {
"1": {
"name": "entity.tag.tagbraces.django"
},
"2": {
"name": "constant.other.tag.name.django"
}
}
}
]
},
"django-template-filter": {
"patterns": [
{
"match": "(\\|) ?(add|addslashes|capfirst|center|cut|date|default|default_if_none|dictsort|dictsortreversed|divisibleby|escape|escapejs|filesizeformat|first|fix_ampersands|floatformat|force_escape|get_digit|iriencode|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|ljust|lower|make_list|phone2numeric|pluralize|pprint|random|removetags|rjust|safe|safeseq|slice|slugify|stringformat|striptags|time|timesince|timeutil|title|truncatewords|truncatewords_html|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|yesno|apnumber|intcomma|intword|naturalday|ordinal|STATIC_PREFIX)\\b",
"name": "entity.name.function.filter.django",
"captures": {
"1": {
"name": "entity.tag.filter-pipe.django"
},
"2": {
"name": "keyword.control.filter.django"
}
}
},
{
"match": "(\\|)([a-zA-Z0-9_]+)\\b",
"name": "entity.name.function.filter.django",
"captures": {
"1": {
"name": "entity.tag.filter-pipe.django"
},
"2": {
"name": "constant.other.filter.django"
}
}
},
{
"match": ":",
"name": "keyword.operator.filter-argument.django"
},
{
"match": "=",
"name": "keyword.operator.assignment.django"
},
{
"match": ",",
"name": "keyword.operator.argument-separator.django"
},
{
"match": "\\.",
"name": "keyword.operator.getter.django"
},
{
"match": "[a-zA-Z0-9_]+",
"name": "string.unquoted.tag-string.django"
},
{
"match": "(>=|>|==|!=|<|<=|and|or|is|not|in)",
"name": "keyword.operator.condition.django"
},
{
"include": "#string-double-quoted"
},
{
"include": "#string-single-quoted"
}
]
},
"django-template-tag-contrib": {
"patterns": [
{
"match": "\\b(cache|i18n|l10n|static|tz|flatpages|humanize|admin_list|admin_modify|admin_static|admin_urls|base|log)\\b",
"name": "constant.other.contrib.django"
}
]
},
"django-template-tag": {
"patterns": [
{
"match": "\\b(and|or|not|is|in|by|as|asvar|trimmed|with)\\b",
"name": "keyword.operator.django"
}
]
},
"string-double-quoted": {
"begin": "\"",
"end": "\"",
"name": "string.quoted.double.html",
"patterns": [
{
"include": "#django-stuff"
},
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
},
"string-single-quoted": {
"begin": "'",
"end": "'",
"name": "string.quoted.single.html",
"patterns": [
{
"include": "#entities"
}
],
"beginCaptures": {
"0": {
"name": "punctuation.definition.string.begin.html"
}
},
"endCaptures": {
"0": {
"name": "punctuation.definition.string.end.html"
}
}
},
"entities": {
"patterns": [
{
"match": "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)",
"name": "constant.character.entity.html",
"captures": {
"1": {
"name": "punctuation.definition.entity.html"
},
"3": {
"name": "punctuation.definition.entity.html"
}
}
},
{
"match": "&",
"name": "invalid.illegal.bad-ampersand.html"
}
]
}
},
"patterns": [
{
"include": "#django-stuff"
}
]
}

View File

@ -0,0 +1,18 @@
name = "Django txt"
scopeName = "text.django"
fileTypes = ["txt"]
repositories = [
"django-stuff",
"django-template-filter",
"django-template-tag-contrib",
"django-template-tag",
"string-double-quoted",
"string-single-quoted",
"entities",
]
[repository]
[[patterns]]
include = "#django-stuff"

View File

@ -0,0 +1,69 @@
[[patterns]]
begin = "{%\\s*comment ?(\"([\\w\\s])*\" )?\\s*%}"
end = "{%\\s*endcomment\\s*%}"
name = "comment.block.django"
[[patterns]]
match = "{#.*#}"
name = "comment.line.number-sign.django"
[[patterns]]
begin = "{{"
end = "}}"
name = "storage.type.variable.django"
[patterns.captures.0]
name = "entity.tag.tagbraces.django"
[[patterns.patterns]]
include = "#django-template-filter"
[[patterns]]
begin = "({%)\\s*(load) "
end = "(%})"
name = "storage.type.templatetag.django"
[patterns.captures.1]
name = "entity.tag.tagbraces.django"
[patterns.captures.2]
name = "keyword.control.tag-name.django"
[[patterns.patterns]]
include = "#django-template-tag-contrib"
[[patterns.patterns]]
include = "#django-template-filter"
[[patterns]]
begin = "({%)\\s*(autoescape|endautoescape|block|endblock|blocktrans|endblocktrans|trans|plural|comment|endcomment|debug|extends|filter|firstof|for|empty|endfor|if|elif|else|endif|include|ifchanged|endifchanged|ifequal|endifequal|ifnotequal|endifnotequal|from|low|regroup|ssi|spaceless|endspaceless|templatetag|widthratio|with|endwith|csrf_token|cycle|url|verbatim|endverbatim|lorem|thumbnail|endthumbnail|get_static_prefix)\\b"
end = "(%})"
name = "storage.type.templatetag.django"
[patterns.captures.1]
name = "entity.tag.tagbraces.django"
[patterns.captures.2]
name = "keyword.control.tag-name.django"
[[patterns.patterns]]
include = "#string-double-quoted"
[[patterns.patterns]]
include = "#string-single-quoted"
[[patterns.patterns]]
include = "#django-template-tag"
[[patterns.patterns]]
include = "#django-template-filter"
[[patterns]]
begin = "({%)\\s*([a-zA-Z0-9_.]+)"
end = "(%})"
name = "storage.type.customtemplatetag.django"
[patterns.captures.1]
name = "entity.tag.tagbraces.django"
[patterns.captures.2]
name = "constant.other.tag.name.django"
[[patterns.patterns]]
include = "#string-double-quoted"
[[patterns.patterns]]
include = "#string-single-quoted"
[[patterns.patterns]]
include = "#django-template-tag"
[[patterns.patterns]]
include = "#django-template-filter"

View File

@ -0,0 +1,48 @@
[[patterns]]
match = "(\\|) ?(add|addslashes|capfirst|center|cut|date|default|default_if_none|dictsort|dictsortreversed|divisibleby|escape|escapejs|filesizeformat|first|fix_ampersands|floatformat|force_escape|get_digit|iriencode|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|ljust|lower|make_list|phone2numeric|pluralize|pprint|random|removetags|rjust|safe|safeseq|slice|slugify|stringformat|striptags|time|timesince|timeutil|title|truncatewords|truncatewords_html|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|yesno|apnumber|intcomma|intword|naturalday|ordinal|STATIC_PREFIX)\\b"
name = "entity.name.function.filter.django"
[patterns.captures.1]
name = "entity.tag.filter-pipe.django"
[patterns.captures.2]
name = "keyword.control.filter.django"
[[patterns]]
match = "(\\|)([a-zA-Z0-9_]+)\\b"
name = "entity.name.function.filter.django"
[patterns.captures.1]
name = "entity.tag.filter-pipe.django"
[patterns.captures.2]
name = "constant.other.filter.django"
[[patterns]]
match = ":"
name = "keyword.operator.filter-argument.django"
[[patterns]]
match = "="
name = "keyword.operator.assignment.django"
[[patterns]]
match = ","
name = "keyword.operator.argument-separator.django"
[[patterns]]
match = "\\."
name = "keyword.operator.getter.django"
[[patterns]]
match = "[a-zA-Z0-9_]+"
name = "string.unquoted.tag-string.django"
[[patterns]]
match = "(>=|>|==|!=|<|<=|and|or|is|not|in)"
name = "keyword.operator.condition.django"
[[patterns]]
include = "#string-double-quoted"
[[patterns]]
include = "#string-single-quoted"

View File

@ -0,0 +1,4 @@
[[patterns]]
match = "\\b(cache|i18n|l10n|static|tz|flatpages|humanize|admin_list|admin_modify|admin_static|admin_urls|base|log)\\b"
name = "constant.other.contrib.django"

View File

@ -0,0 +1,4 @@
[[patterns]]
match = "\\b(and|or|not|is|in|by|as|asvar|trimmed|with)\\b"
name = "keyword.operator.django"

View File

@ -0,0 +1,12 @@
[[patterns]]
match = "(&)([a-zA-Z0-9]+|#[0-9]+|#x[0-9a-fA-F]+)(;)"
name = "constant.character.entity.html"
[patterns.captures.1]
name = "punctuation.definition.entity.html"
[patterns.captures.3]
name = "punctuation.definition.entity.html"
[[patterns]]
match = "&"
name = "invalid.illegal.bad-ampersand.html"

View File

@ -0,0 +1,16 @@
begin = "\""
end = "\""
name = "string.quoted.double.html"
[beginCaptures.0]
name = "punctuation.definition.string.begin.html"
[endCaptures.0]
name = "punctuation.definition.string.end.html"
[[patterns]]
include = "#django-stuff"
[[patterns]]
include = "#entities"

View File

@ -0,0 +1,13 @@
begin = "'"
end = "'"
name = "string.quoted.single.html"
[beginCaptures.0]
name = "punctuation.definition.string.begin.html"
[endCaptures.0]
name = "punctuation.definition.string.end.html"
[[patterns]]
include = "#entities"

View File

@ -0,0 +1,2 @@
match = "\\b([a-zA-Z\\-:]+)"
name = "entity.other.attribute-name.html"

View File

@ -0,0 +1,41 @@
begin = "\\b(id)\\b\\s*(=)"
end = "(?<='|\")"
name = "meta.attribute-with-value.id.html"
[captures.1]
name = "entity.other.attribute-name.id.html"
[captures.2]
name = "punctuation.separator.key-value.html"
[[patterns]]
begin = "\""
contentName = "meta.toc-list.id.html"
end = "\""
name = "string.quoted.double.html"
[patterns.beginCaptures.0]
name = "punctuation.definition.string.begin.html"
[patterns.endCaptures.0]
name = "punctuation.definition.string.end.html"
[[patterns.patterns]]
include = "#django-stuff"
[[patterns.patterns]]
include = "#entities"
[[patterns]]
begin = "'"
contentName = "meta.toc-list.id.html"
end = "'"
name = "string.quoted.single.html"
[patterns.beginCaptures.0]
name = "punctuation.definition.string.begin.html"
[patterns.endCaptures.0]
name = "punctuation.definition.string.end.html"
[[patterns.patterns]]
include = "#entities"

View File

@ -0,0 +1,12 @@
[[patterns]]
include = "#tag-id-attribute"
[[patterns]]
include = "#tag-generic-attribute"
[[patterns]]
include = "#string-double-quoted"
[[patterns]]
include = "#string-single-quoted"

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="python-extension-pack" Version="1.7.0" Publisher="donjayamanne" />
<DisplayName>Python Extension Pack</DisplayName>
<Description xml:space="preserve">Popular Visual Studio Code extensions for Python</Description>
<Tags>python,django,debugger,unittest,jinja,__web_extension</Tags>
<Categories>Extension Packs</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.62.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="njpwerner.autodocstring,ms-python.python,wholroyd.jinja,batisteo.vscode-django,VisualStudioExptTeam.vscodeintellicode,KevinRose.vsc-python-indent,donjayamanne.python-environment-manager" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace,web" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/DonJayamanne/python-extension-pack.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/DonJayamanne/python-extension-pack.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/DonJayamanne/python-extension-pack.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/DonJayamanne/python-extension-pack/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/DonJayamanne/python-extension-pack#readme" />
<Property Id="Microsoft.VisualStudio.Services.Branding.Color" Value="#1e415e" />
<Property Id="Microsoft.VisualStudio.Services.Branding.Theme" Value="dark" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/icon.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/icon.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@ -0,0 +1,34 @@
## 1.7.0
- Add Python Indent
- Add Python Environment Manager
- Add Jupyter extension.
## 1.5.0
- Remove Jupyter extension.
## 1.4.0
- Remove VS Live Share (as this not specific to Python).
## 1.3.0
- Included Visual Studio IntelliCode.
## 1.2.0
- Included VS Live Share.
- Update recommented Django extensions.
## 1.1.0
- Updated to change the id of the Python extension.
## 1.0.0
- Oops, fixed repo links.
## 0.0.1
- Initial release.

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 DonJayamanne
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,20 @@
# Python Extension Pack
This extension pack packages some of the most popular (and some of my favorite) Python extensions.
## Extensions Included
* [Python](https://marketplace.visualstudio.com/items?itemName=ms-python.python) - Linting, Debugging (multi-threaded, remote), Intellisense, code formatting, refactoring, unit tests, snippets, Data Science (with Jupyter), PySpark and more.
* [Jinja](https://marketplace.visualstudio.com/items?itemName=wholroyd.jinja) - Jinja template language support for Visual Studio Code.
* [Django](https://marketplace.visualstudio.com/items?itemName=batisteo.vscode-django) - Beautiful syntax and scoped snippets for perfectionists with deadlines.
* [Visual Studio IntelliCode](https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode) - Provides AI-assisted productivity features for Python developers in Visual Studio Code with insights based on understanding your code combined with machine learning..
* [Python Environment Manager](https://marketplace.visualstudio.com/items?itemName=donjayamanne.python-environment-manager) - Provides the ability to view and manage all of your Python environments & packages from a single place.
* [Python Docstring Generator](https://marketplace.visualstudio.com/items?itemName=njpwerner.autodocstring) - Quickly insert Python comment blocks with contextually inferred parameters for classes and methods based on multiple, selectable template patterns.
* [Python Indent](https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent) - Correct python indentation in Visual Studio Code.
* [Jupyter](https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter) - Provides Jupyter notebook support for Python language, used for data science, scientific computing and machine learning.
## Want to see your extension added?
Open a PR and I'd be happy to take a look.
**Enjoy!**

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -0,0 +1,59 @@
{
"name": "python-extension-pack",
"displayName": "Python Extension Pack",
"description": "Popular Visual Studio Code extensions for Python",
"version": "1.7.0",
"publisher": "donjayamanne",
"author": {
"name": "Don Jayamanne",
"email": "don.jayamanne@yahoo.com"
},
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/DonJayamanne/python-extension-pack"
},
"bugs": {
"url": "https://github.com/DonJayamanne/python-extension-pack/issues"
},
"icon": "icon.png",
"galleryBanner": {
"color": "#1e415e",
"theme": "dark"
},
"engines": {
"vscode": "^1.62.0"
},
"keywords": [
"python",
"django",
"debugger",
"unittest",
"jinja"
],
"categories": [
"Extension Packs"
],
"extensionPack": [
"njpwerner.autodocstring",
"ms-python.python",
"wholroyd.jinja",
"batisteo.vscode-django",
"VisualStudioExptTeam.vscodeintellicode",
"KevinRose.vsc-python-indent",
"donjayamanne.python-environment-manager"
],
"__metadata": {
"id": "f5188937-53e0-45bb-a16d-61231003fa3b",
"publisherId": "1ba8bd00-2ad1-4be0-a007-5b4b954c1ee7",
"publisherDisplayName": "Don Jayamanne",
"targetPlatform": "undefined",
"isApplicationScoped": false,
"isPreReleaseVersion": false,
"hasPreReleaseVersion": false,
"installedTimestamp": 1718224747250,
"pinned": false,
"preRelease": false,
"source": "gallery"
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: kbrose
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
custom: ['https://paypal.me/2kbrose']

View File

@ -0,0 +1,33 @@
---
name: Suboptimal Indentation
about: Indentation is not working as desired
title: ''
labels: indentation
assignees: ''
---
What the code looks like before pressing `enter`:
```python
def my_example():
pass| # Use "|" to show where the cursor is. Keep your code nice and short!
```
What I want the code to look like after pressing `enter`:
```python
def my_example():
pass
|
```
What the code actually looks like after pressing `enter`:
```python
def my_example():
pass
|
```
<!-- Attach any screenshots and/or write additional information below. -->

View File

@ -0,0 +1,8 @@
---
name: Bug report
about: For issues that are not about improving indentation.
title: ''
labels: ''
assignees: ''
---

View File

@ -0,0 +1,8 @@
---
name: Feature request
about: Suggest an idea for this project.
title: ''
labels: ''
assignees: ''
---

View File

@ -0,0 +1,10 @@
<!-- Please leave this checklist in your PR -->
**Checklist**
* [ ] Relevant issues have been referenced
* [ ] [CHANGELOG.md](../CHANGELOG.md) has been updated (bullet points added to the *Unreleased* section)
* [ ] Tests have been added, or are not relevant
**Description**
<!-- Please describe your PR here -->

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="vsc-python-indent" Version="1.18.0" Publisher="KevinRose" />
<DisplayName>Python Indent</DisplayName>
<Description xml:space="preserve">Correct Python indentation</Description>
<Tags>python,indent,dedent,indentation,whitespace,keybindings,jupyter</Tags>
<Categories>Formatters,Keymaps,Programming Languages</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.65.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="ui,workspace" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/kbrose/vsc-python-indent.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/kbrose/vsc-python-indent.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/kbrose/vsc-python-indent.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/kbrose/vsc-python-indent/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/kbrose/vsc-python-indent#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
<Property Id="Microsoft.VisualStudio.Services.EnableMarketplaceQnA" Value="false" />
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/static/logo.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/static/logo.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@ -0,0 +1,184 @@
# Change Log
### Unreleased
### 1.18.0
* When using remote development, prefer to run extension on the host (instead of locally) to reduce lag.
### 1.17.0
* Fix dedent logic when the dedent keyword is preceded by a multi-line indent keyword
### 1.16.0
* Correctly delete selected text starting with space (fixes [#96](https://github.com/kbrose/vsc-python-indent/issues/96))
### 1.15.0
* Update CI system to use Node v16
* Update minimum compatible VSC version to 1.65
* Update development dependencies, including migrating linting from tslint to eslint
* State the name of the theme used in the demo gif
### 1.14.2
* Fixes changelog - I mislabeled several releases.
### 1.14.1
* Handles neovim mode in a similar way to vim mode.
* Fix innacuracy in README around comment-line continuation.
### 1.14.0
* Update CI system to use Node v10.
* Add GitHub Sponsors information
### 1.13.1
* Fix link in readme
### 1.13.0
* This extension has been installed half a million times. That's pretty cool.
* Added dontation link.
* Updated development-only dependency `y18n`.
* Refactored code to make it more testable.
### 1.12.0
* Updated required vscode version to 1.50 (September 2020 release)
* Whitespace to the right of the cursor will now be deleted when pressing `Enter`. This only happens when there are non-whitespace characters to the left and to the right of the cursor, e.g. as in the case `def f(x,| y)` (where `|` is the cursor) but not in the case ` |print(x)` or `print(x)| `. See [the issue](https://github.com/kbrose/vsc-python-indent/issues/62) for more information.
### 1.11.0
* Add option to control whether hanging indents put the bracket on its own line or not.
### 1.10.1
* Update README to showcase new setting `trimLinesWithOnlyWhitespace`.
### 1.10.0
* Lines that contain only whitespace can be trimmed after pressing `Enter` using the new `trimLinesWithOnlyWhitespace` setting. This defaults to `false`, i.e. lines are _not_ trimmed. The new behavior more closely matches the native VSCode behavior. See [issue 60](https://github.com/kbrose/vsc-python-indent/issues/60) for a more complete discussion of the native behavior.
* Update development dependencies to handle various deprecations and migrations. This has no effect on the dependencies needed to _run_ the extension, just _develop_ it.
### 1.9.0
The access token I use to publish this extension expired, which meant this version didn't get pushed. However, it still incremented the version number. `¯\_(ツ)_/¯`
### 1.8.1
* Also handle `while...else`.
### 1.8.0
* Update dedent logic to handle `for...else` and `try...else` constructs.
### 1.7.0
* Scroll the window when pressing `Enter` near the bottom of the window/out of view.
### 1.6.1
* Bump `python-indent-parser` version
### 1.6.0
* Fix incorrect indentation when pressing enter on lines with an open bracket, but no closing bracket.
* Switch to centralized python parsing library shared by the `python-indent` package for Atom (thanks @DSpeckhals)
### 1.5.0
* If your cursor is in the middle of a comment when you press `Enter`, then the next line is auto-commented as well.
* Small updates to README
### 1.4.0
* Better dedentation handling of `else:` and similar keywords.
### 1.3.0
* Indent the next line if you use a backslash to do a line continuation.
### 1.2.0
* Fix unexpected dedenting when you have variables named like `return_this_value`.
### 1.1.0
* The extension now works if text is highlighted when the user presses Enter. Thanks @WhistleWhileYouWork!
### 1.0
* The package has been deemed stable enough for 1.0!
### 0.9.0
* Dedent current line on `else`, `elif`, `except`, and `finally` statements
* ***NOTE:*** You no longer need to manually un-indent on these lines, just press `Enter` and it will be un-indented for you.
* Don't dedent on keywords if they appear in triple quoted string.
* Update demo gif to showcase improved dedent behaviors.
### 0.8.1
* Don't run extension if multiple cursors exist.
### 0.8.0
* Don't dedent if the special dedent keyword appears in special contexts, like in strings or as variable names. Thanks @chen19901225 for the inspiration!
* Update required version of vscode to resolve security vulnerability [CVE-2018-20834](https://nvd.nist.gov/vuln/detail/CVE-2018-20834).
### 0.7.0
* Fixed compatibility with the `vscodevim.vim` extension.
### 0.6.1
* Re-syncing github and marketplace code histories
### 0.6.0
* Update hanging indent to work correctly when there is content to the right of your cursor.
### 0.5.3
Merged the [first external pull request](https://github.com/kbrose/vsc-python-indent/pull/7), thanks @chen19901225 🎉
* Fixed complex dedent cases like `return f(x)(y)`.
* Turn on continuous integration for pull requests from forks.
* Run linter in continuous integration.
### 0.5.2
* Documentation updates
### 0.5.1
* Documentation updates
* New logo
### 0.5.0
* Fix hanging indentation
* Add option to make hanging indent snippet-style (press tab to go to ending bracket)
### 0.4.0
* Update keybinding condition to not override `Enter` accepting intellisense suggestion.
### 0.3.0
* Update activation events and keybinding condition to work in unsaved buffers. Resolves [#2](https://github.com/kbrose/vsc-python-indent/issues/2).
### 0.2.0
* Refactor to make testing easier
* Add a bunch of testing, now that it is easier
* Run tests on [Azure Pipeline](https://dev.azure.com/kevinbrose/vsc-python-indent/_build/results).
### 0.1.0
* First pass at correct indentation.
* Preserve un-indenting behavior with `return`, `pass`, etc.

View File

@ -0,0 +1,61 @@
# Developing this extension
Thank you for your interest in helping develop this extension. If you're new to developing VSCode extensions, then you may want to try and follow the quickstart below. If something doesn't work, feel free to open an issue or submit a PR fixing this documentation!
## Quickstart
1. Download node / npm
1. From the top level folder, run `npm install` to get the dependencies.
1. Open this folder in vscode.
1. Make your changes
1. You can manually test your changes by navigating to "Run" -> "Run Extension" -> green arrow. This opens a sandboxed version of vscode with your updated extension.
1. You should add unit tests for your functionality as well. These go under `src/test/suite`. *Your PR will likely not be merged without unit tests.*
# Release check list
You will need [`vsce`](https://github.com/Microsoft/vscode-vsce) installed.
There is a long standing bug while authorizing the `vsce` command line tool, set the "Organization" to "All accessible organizations" during token creation even if there is only one organization to work around it.
1. The [CHANGELOG](./CHANGELOG.md) has been updated.
1. `git checkout master`
1. `git pull`
1. Smoke test (run extension through debugger, open `smoke_test.py`, press `enter` after each line and make sure it looks good).
1. CI has passed on the master branch.
1. `vsce publish {patch,minor,major}`
1. `vsce package`
1. `git push; git push --tags`
1. On GitHub, draft a release using the existing tag that was created by the `publish` command.
* If you are not told that the tag already exists, then you have a typo, or you missed a step.
1. Attach the `.vsix` artifact created by the `package` command to the draft release.
1. Publish the release.
# Getting CI to work
Unfortunately, that yaml does not fully specify the build process. There remain settings
which are *uncontrollable from the pipeline yaml*. An export button exists on the azure website,
but it is greyed out and unclickable.
## Triggers
Despite what the [documentation](https://docs.microsoft.com/en-us/azure/devops/pipelines/yaml-schema?view=azure-devops&tabs=schema#triggers)
says, it doesn't seem like Azure Pipelines automatically trigger builds on branches or pull requests.
To get around this, `trigger` and `pr` sections were added to [azure-pipelines.yml](./azure-pipelines.yml).
However, `pr` builds are still not working correctly.
Even with these edits, pull requests from forks are not automatically enabled, and this must be enabled through the GUI.
As of writing, you can do this by going to pipelines page, clicking "Edit" ->
three veritcal dots -> "Triggers" -> "Pull request validation" ->
"Build pull requests from forks of this repository".
It is impossible to schedule builds in the YAML, so that must also be done through the GUI.
## GitHub has old pipeline names
If you update the name of the CI build pipeline, GitHub may continue to expect the old name.
To fix this, go to the repo settings page on GitHub -> branches -> "edit" next to the branch of your choice
-> check/uncheck the desired build names.
# Extension reporting hub
https://marketplace.visualstudio.com/manage/publishers/kevinrose/extensions/vsc-python-indent/hub (requries login)

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Kevin Rose
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,196 @@
# Python Indent
Correct Python indentation in Visual Studio Code. See the extension on the [VSCode Marketplace](https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent&ssr=true) and its source code on [GitHub](https://github.com/kbrose/vsc-python-indent).
![](https://github.com/kbrose/vsc-python-indent/raw/HEAD/static/demo.gif)
Theme used in gif: _Community Theme Palenight_ from [_Community Material Theme_](https://marketplace.visualstudio.com/items?itemName=Equinusocio.vsc-community-material-theme) v1.4.4.
[![Build Status](https://dev.azure.com/kevinbrose/vsc-python-indent/_apis/build/status/vsc-python-indent-CI?branchName=master)](https://dev.azure.com/kevinbrose/vsc-python-indent/_build/latest?definitionId=1&branchName=master)
[![Installs](https://vsmarketplacebadge.apphb.com/installs-short/KevinRose.vsc-python-indent.svg)](https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent)
[![Stars](https://vsmarketplacebadge.apphb.com/rating-star/KevinRose.vsc-python-indent.svg)](https://marketplace.visualstudio.com/items?itemName=KevinRose.vsc-python-indent)
## How it works
Every time you press the `Enter` key in a Python context, this extension will parse your Python file up to the location of your cursor, and determine exactly how much the next line (or two in the case of hanging indents) should be indented and how much nearby lines should be un-indented.
There are three main cases when determining the correct indentation, described below.
### Between bracket pairs
In cases when you have your cursor between an open bracket (one of `[({`) and its closing bracket pair (the corresponding one of `})]`), this extension will keep subsequent lines indented just to the right of where it was opened:
```python
data = {'a': 0,
| # <- pressing enter should put your cursor at the "|"
| # <- this is where default VS Code puts your cursor
```
Even heavily nested brackets are handled:
```python
data = {'a': 0,
'b': [[1, 2],
| # <- match the more recently opened [ instead of the {
| # <- default behavior of VS Code
```
```python
data = {'a': 0,
'b': [[1, 2],
[3, 4]],
| # <- since the lists are all closed, go back to the { position
| # <- default behavior of VS Code
```
```python
data = {'a': 0,
'b': [[1, 2],
[3, 4]],
'c': 5}
| # <- go back to indentation level before any brackets were opened
| # <- default behavior of VS Code
```
In the full example below, default VS Code required nine extra key presses (three tabs, two spaces, and four backspaces) to match the *automatic* indentation of this extension.
```python
data = {'a': 0,
'b': [[1, 2],
[3, 4]],
'c': 5}
done(data)
```
### Hanging indents
When you have opened a bracket, but not yet inserted any content, pressing `Enter` will create a hanging indent, matching the base behavior of VS Code.
```python
result = my_func(
| # <- your cursor should end up here
) # <- the closing bracket should end up here
```
You can use the setting `useTabOnHangingIndent` to make it so that when you are done typing you can simply press `Tab` to be taken to the closing bracket.
If there is content to the right of your cursor when you press `Enter`, then this extension falls back on just indenting by your set tab size.
```python
# The "|" is your cursor's location
result = my_func(|x, y, z)
# and when you press Enter...
result = my_func(
|x, y, z)
```
If you never want to have the closing bracket end up on its own line (i.e. you always want to just indent by the set tab size), use the `keepHangingBracketOnLine` configuration setting. *Warning:* This may cause confusing indentation with function definitions as the argument list and the function code may end up at the same indentation level.
It's not often used, but a backslash to continue a line will also result in the next line being indented.
```python
my_long_calculation = 1234 + \
5678
```
### Keywords
Some keywords in Python imply certain indentation behaviors. For example, if there is a `return` statement, then we know the next line can be un-indented (or *de*dented) since no statements can follow a `return` in the same code block. Other keywords that follow the same pattern are `pass`, `break`, `continue`, and `raise`.
Similarly, if there is an `else:` on the current line, then the current line needs to be dedented, and the next line needs to be indented *relative to* the new position of the `else:`. Other keywords that follow the same pattern are `elif ...:`, `except ...:`, and `finally:`. Some examples are shown below.
```python
if True:
pass
else:|
# and when you press Enter...
if True:
pass
else:
|
```
But if you have manually changed the indentation, the extension should not change it for you:
```python
if True:
if True:
pass
else:|
# and when you press Enter, do NOT dedent!
if True:
if True:
pass
else:
|
# Or even more nested
if True:
if True:
if True:
pass
else:|
# and when you press Enter, still do NOT dedent
if True:
if True:
if True:
pass
else:
|
```
## Extending comments
If (and only if) you press `Enter` while your cursor is in the middle of a comment, then the next line will automatically be made into a comment.
```python
# As always, the "|" indicates your cursor
def f():
# This function is |gonna be REAL good!
def f():
# This function is
# |gonna be REAL good
```
## Trimming whitespace lines
You can trim whitespace from lines that contain *only* whitespace by using the `trimLinesWithOnlyWhitespace` configuration setting (the default is to not trim whitespace in this way). This setting brings the behavior closer to native VSCode behavior.
```python
# In the below code, the character "·" represents a space
def f():
····|
# The default of false preserves whitespace
def f():
····
····|
# Setting trimLinesWithOnlyWhitespace = true will trim the whitespace
def f():
····|
```
## Why is it needed?
There are many related issues on GitHub ([[1]](https://github.com/Microsoft/vscode-python/issues/481), [[2]](https://github.com/Microsoft/python-language-server/issues/671), [[3]](https://github.com/Microsoft/vscode/issues/66235), [[4]](https://github.com/Microsoft/vscode-python/issues/684), [[5]](https://github.com/Microsoft/vscode-python/issues/539)) asking for improved Python indentation in VS Code. It seems like the maintainers of the Python extension at microsoft are not prioritizing indentation, since there has been no progress in the years since it was first asked for.
## Caveats
Known caveats are listed below.
* Using tabs (`\t`) for your indentation will not work.
* If your Python code is not correctly formatted, you may not get correct indentation.
* The extension works by registering the `Enter` key as a keyboard shortcut. The conditions when the shortcut is triggered have been heavily restricted, but there may still be times this extension is unexpectedly overriding `Enter` behavior. Specifically, `vim` related plugins seem to require special attention. See the [`when`](https://code.visualstudio.com/api/references/when-clause-contexts) clause in [package.json](https://github.com/kbrose/vsc-python-indent/blob/HEAD/package.json).
If you experience any problems, please submit an [issue](https://github.com/kbrose/vsc-python-indent/issues), or better yet a [pull request](https://github.com/kbrose/vsc-python-indent/pulls).
## Release Notes
See the [change log](https://github.com/kbrose/vsc-python-indent/blob/HEAD/CHANGELOG.md).
## Developing
See the [developer docs](https://github.com/kbrose/vsc-python-indent/blob/HEAD/DEVELOP.md) for pointers on how to develop this extension.

View File

@ -0,0 +1,40 @@
trigger:
- master
pr:
branches:
include:
- '*'
strategy:
matrix:
linux:
imageName: 'ubuntu-latest'
mac:
imageName: 'macos-latest'
windows:
imageName: 'windows-latest'
pool:
vmImage: $(imageName)
steps:
- task: NodeTool@0
inputs:
versionSpec: '16.x'
displayName: 'Install Node.js'
- bash: |
/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
echo ">>> Started xvfb"
displayName: Start xvfb
condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))
- bash: |
echo ">>> Compile vscode-test"
yarn && yarn compile
echo ">>> Compiled vscode-test"
cd sample
echo ">>> Run sample integration test"
yarn && yarn compile && yarn test
displayName: Run Tests
env:
DISPLAY: ':99.0'

View File

@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.deactivate = exports.activate = void 0;
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const vscode = require("vscode");
const indent_1 = require("./indent");
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
function activate(context) {
let disposable = vscode.commands.registerTextEditorCommand('pythonIndent.newlineAndIndent', indent_1.newlineAndIndent);
context.subscriptions.push(disposable);
}
exports.activate = activate;
// this method is called when your extension is deactivated
function deactivate() { }
exports.deactivate = deactivate;
//# sourceMappingURL=extension.js.map

View File

@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.startingWhitespaceLength = exports.trimCurrentLine = exports.currentLineDedentation = exports.extendCommentToNextLine = exports.editsToMake = exports.newlineAndIndent = void 0;
const vscode = require("vscode");
const parser_1 = require("./parser");
function newlineAndIndent(textEditor, edit, args) {
// Get rid of any user selected text, since a selection is
// always deleted whenever ENTER is pressed.
// This should always happen first
if (!textEditor.selection.isEmpty) {
edit.delete(textEditor.selection);
// Make sure we get rid of the selection range.
textEditor.selection = new vscode.Selection(textEditor.selection.end, textEditor.selection.end);
}
const position = textEditor.selection.active;
const tabSize = textEditor.options.tabSize;
const insertionPoint = new vscode.Position(position.line, position.character);
const currentLine = textEditor.document.lineAt(position).text;
let snippetCursor = '$0';
let settings = vscode.workspace.getConfiguration('pythonIndent');
if (settings.useTabOnHangingIndent) {
snippetCursor = '$1';
}
let hanging = parser_1.Hanging.none;
let toInsert = '\n';
try {
if (textEditor.document.languageId === 'python') {
const lines = textEditor.document.getText(new vscode.Range(0, 0, position.line, position.character)).split("\n");
const edits = editsToMake(lines, currentLine, tabSize, position.line, position.character, settings.trimLinesWithOnlyWhitespace, settings.keepHangingBracketOnLine);
toInsert = edits.insert;
edits.deletes.forEach(range => { edit.delete(range); });
hanging = edits.hanging;
}
}
finally {
// we never ever want to crash here, fallback on just inserting newline
if (hanging === parser_1.Hanging.full) {
// Hanging indents end up with the cursor in a bad place if we
// just use the edit.insert() function, snippets behave better.
// The VSCode snippet logic already does some indentation handling,
// so don't use the toInsert, just ' ' * tabSize.
// That behavior is not documented.
textEditor.insertSnippet(new vscode.SnippetString('\n' + ' '.repeat(tabSize) + snippetCursor + '\n'));
}
else {
edit.insert(insertionPoint, toInsert);
}
textEditor.revealRange(new vscode.Range(position, new vscode.Position(position.line + 2, 0)));
}
}
exports.newlineAndIndent = newlineAndIndent;
function editsToMake(lines, currentLine, tabSize, lineNum, charNum, trimLinesWithOnlyWhitespace, keepHangingBracketOnLine) {
let { nextIndentationLevel: indent, parseOutput: parseOut } = (0, parser_1.indentationInfo)(lines, tabSize);
let deletes = [];
// If cursor has whitespace to the right, followed by non-whitespace,
// and also has non-whitespace to the left, then trim the whitespace to the right
// of the cursor. E.g. in cases like "def f(x,| y):"
const numCharsToDelete = startingWhitespaceLength(currentLine.slice(charNum));
if ((numCharsToDelete > 0) && (/\S/.test(currentLine.slice(0, charNum)))) {
deletes.push(new vscode.Range(lineNum, charNum, lineNum, charNum + numCharsToDelete));
}
const dedentAmount = currentLineDedentation(lines, tabSize, parseOut);
const shouldTrim = trimCurrentLine(lines[lines.length - 1], trimLinesWithOnlyWhitespace);
if ((dedentAmount > 0) || shouldTrim) {
const totalDeleteAmount = shouldTrim ? lines[lines.length - 1].length : dedentAmount;
deletes.push(new vscode.Range(lineNum, 0, lineNum, totalDeleteAmount));
indent = Math.max(indent - dedentAmount, 0);
}
let hanging = (0, parser_1.shouldHang)(currentLine, charNum);
if (keepHangingBracketOnLine && hanging === parser_1.Hanging.full) {
// The only difference between partial and full is that
// full puts the closing bracket on its own line.
hanging = parser_1.Hanging.partial;
}
let toInsert = '\n';
if (hanging === parser_1.Hanging.partial) {
toInsert = '\n' + ' '.repeat((0, parser_1.indentationLevel)(currentLine) + tabSize);
}
else {
toInsert = '\n' + ' '.repeat(Math.max(indent, 0));
}
if (extendCommentToNextLine(currentLine, charNum)) {
toInsert = toInsert + '# ';
}
return { insert: toInsert, deletes: deletes, hanging: hanging };
}
exports.editsToMake = editsToMake;
// Current line is a comment line, and we should make the next one commented too.
function extendCommentToNextLine(line, pos) {
if (line.trim().startsWith('#') && line.slice(pos).trim().length && line.slice(0, pos).trim().length) {
return true;
}
return false;
}
exports.extendCommentToNextLine = extendCommentToNextLine;
// Returns the number of spaces that should be removed from the current line
function currentLineDedentation(lines, tabSize, parseOut) {
const dedentKeywords = { elif: ["if"], else: ["if", "try", "for", "while"], except: ["try"], finally: ["try"] };
// Reverse to help searching, use slice() to copy since reverse() is inplace
const line = lines[lines.length - 1];
const trimmed = line.trim();
if (trimmed.endsWith(":")) {
for (const keyword of Object.keys(dedentKeywords).filter((key) => trimmed.startsWith(key))) {
const matchingLineNumber = Math.max(...dedentKeywords[keyword].map((indentKeyword) => {
return parseOut.lastSeenIndenters[indentKeyword];
}));
if (matchingLineNumber >= 0) {
const currentIndent = (0, parser_1.indentationLevel)(line);
const matchedIndent = (0, parser_1.indentationLevel)(lines[matchingLineNumber]);
return Math.max(0, Math.min(tabSize, currentIndent, currentIndent - matchedIndent));
}
}
}
return 0;
}
exports.currentLineDedentation = currentLineDedentation;
// Returns true if the current line should have all of its characters deleted.
function trimCurrentLine(line, trimLinesWithOnlyWhitespace) {
if (trimLinesWithOnlyWhitespace) {
if (line.trim().length === 0) {
// That means the string contained only whitespace.
return true;
}
}
return false;
}
exports.trimCurrentLine = trimCurrentLine;
// Returns the number of whitespace characters until the next non-whitespace char
// If there are no non-whitespace chars, returns 0, regardless of number of whitespace chars.
function startingWhitespaceLength(line) {
var _a, _b;
return (_b = (_a = /\S/.exec(line)) === null || _a === void 0 ? void 0 : _a.index) !== null && _b !== void 0 ? _b : 0;
}
exports.startingWhitespaceLength = startingWhitespaceLength;
//# sourceMappingURL=indent.js.map

View File

@ -0,0 +1,320 @@
"use strict";
// Adapted from https://github.com/DSpeckhals/python-indent-parser
Object.defineProperty(exports, "__esModule", { value: true });
exports.shouldHang = exports.indentationInfo = exports.nextIndentationLevel = exports.indentationLevel = exports.Hanging = void 0;
var Hanging;
(function (Hanging) {
Hanging[Hanging["none"] = 0] = "none";
Hanging[Hanging["partial"] = 1] = "partial";
Hanging[Hanging["full"] = 2] = "full";
})(Hanging = exports.Hanging || (exports.Hanging = {}));
function indentationLevel(line) {
return line.search(/\S|$/);
}
exports.indentationLevel = indentationLevel;
function nextIndentationLevel(parseOutput, lines, tabSize) {
const row = lines.length - 1;
// openBracketStack: A stack of [row, col] pairs describing where open brackets are
// lastClosedRow: Either empty, or an array [rowOpen, rowClose] describing the rows
// where the last bracket to be closed was opened and closed.
// lastColonRow: The last row a def/for/if/elif/else/try/except etc. block started
// dedentNext: Boolean, should we dedent the next row?
const { openBracketStack, lastClosedRow, lastColonRow, dedentNext, } = parseOutput;
if (dedentNext && !openBracketStack.length) {
return indentationLevel(lines[row]) - tabSize;
}
if (!openBracketStack.length) {
// Can assume lastClosedRow is not empty
if (lastClosedRow[1] === row) {
// We just closed a bracket on the row, get indentation from the
// row where it was opened
let indentLevel = indentationLevel(lines[lastClosedRow[0]]);
if (lastColonRow === row) {
// We just finished def/for/if/elif/else/try/except etc. block,
// need to increase indent level by 1.
indentLevel += tabSize;
}
return indentLevel;
}
if (lastColonRow === row) {
return indentationLevel(lines[row]) + tabSize;
}
return indentationLevel(lines[row]);
}
if (lastColonRow === row) {
return indentationLevel(lines[row]) + tabSize;
}
// At this point, we are guaranteed openBracketStack is non-empty,
// which means that we are currently in the middle of an opened/closed
// bracket.
const lastOpenBracketLocation = openBracketStack[openBracketStack.length - 1];
// Get some booleans to help work through the cases
// haveClosedBracket is true if we have ever closed a bracket
const haveClosedBracket = lastClosedRow.length > 0;
// justOpenedBracket is true if we opened a bracket on the row we just finished
const justOpenedBracket = lastOpenBracketLocation[0] === row;
// justClosedBracket is true if we closed a bracket on the row we just finished
const justClosedBracket = haveClosedBracket && lastClosedRow[1] === row;
// closedBracketOpenedAfterLineWithCurrentOpen is an ***extremely*** long name, and
// it is true if the most recently closed bracket pair was opened on
// a line AFTER the line where the current open bracket
const closedBracketOpenedAfterLineWithCurrentOpen = haveClosedBracket
&& lastClosedRow[0] > lastOpenBracketLocation[0];
let indentColumn;
if (!justOpenedBracket && !justClosedBracket) {
// The bracket was opened before the previous line,
// and we did not close a bracket on the previous line.
// Thus, nothing has happened that could have changed the
// indentation level since the previous line, so
// we should use whatever indent we are given.
return indentationLevel(lines[row]);
}
else if (justClosedBracket && closedBracketOpenedAfterLineWithCurrentOpen) {
// A bracket that was opened after the most recent open
// bracket was closed on the line we just finished typing.
// We should use whatever indent was used on the row
// where we opened the bracket we just closed. This needs
// to be handled as a separate case from the last case below
// in case the current bracket is using a hanging indent.
// This handles cases such as
// x = [0, 1, 2,
// [3, 4, 5,
// 6, 7, 8],
// 9, 10, 11]
// which would be correctly handled by the case below, but it also correctly handles
// x = [
// 0, 1, 2, [3, 4, 5,
// 6, 7, 8],
// 9, 10, 11
// ]
// which the last case below would incorrectly indent an extra space
// before the "9", because it would try to match it up with the
// open bracket instead of using the hanging indent.
indentColumn = indentationLevel(lines[lastClosedRow[0]]);
}
else {
// lastOpenBracketLocation[1] is the column where the bracket was,
// so need to bump up the indentation by one
indentColumn = lastOpenBracketLocation[1] + 1;
}
return indentColumn;
}
exports.nextIndentationLevel = nextIndentationLevel;
function parseLines(lines) {
// openBracketStack is an array of [row, col] indicating the location
// of the opening bracket (square, curly, or parentheses)
const openBracketStack = [];
// lastClosedRow is either empty or [rowOpen, rowClose] describing the
// rows where the latest closed bracket was opened and closed.
let lastClosedRow = [];
// If we are in a string, this tells us what character introduced the string
// i.e., did this string start with ' or with "?
let stringDelimiter = null;
// This is the row of the last function definition
let lastColonRow = NaN;
// true if we are in a triple quoted string
let inTripleQuotedString = false;
// If we have seen two of the same string delimiters in a row,
// then we have to check the next character to see if it matches
// in order to correctly parse triple quoted strings.
let checkNextCharForString = false;
// true if we should dedent the next row, false otherwise
let dedentNext = false;
// true if we should have a hanging indent, false otherwise
let canHang = false;
// if we see these words at the beginning of a line, dedent the next one
const dedentNextKeywords = [/^\s*return\b/, /^\s*pass\b/, /^\s*break\b/, /^\s*continue\b/, /^\s*raise\b/];
// shows the last seen instance of these indenting keywords
let lastSeenIndents = { if: -1, for: -1, try: -1, while: -1 };
// NOTE: this parsing will only be correct if the python code is well-formed
// statements like "[0, (1, 2])" might break the parsing
// loop over each line
const linesLength = lines.length;
for (let row = 0; row < linesLength; row += 1) {
const line = lines[row];
dedentNext = (stringDelimiter === null) && dedentNextKeywords.some((word) => line.search(word) >= 0);
const trimmed = line.trimStart();
if (trimmed.startsWith("if")) {
lastSeenIndents.if = row;
}
else if (trimmed.startsWith("for")) {
lastSeenIndents.for = row;
}
else if (trimmed.startsWith("try")) {
lastSeenIndents.try = row;
}
else if (trimmed.startsWith("while")) {
lastSeenIndents.while = row;
}
// Keep track of the number of consecutive string delimiter's we've seen
// in this line; this is used to tell if we are in a triple quoted string
let numConsecutiveStringDelimiters = 0;
// boolean, whether or not the current character is being escaped
// applicable when we are currently in a string
let isEscaped = false;
// This is the last defined def/for/if/elif/else/try/except row
const lastlastColonRow = lastColonRow;
const lineLength = line.length;
for (let col = 0; col < lineLength; col += 1) {
const c = line[col];
if (c === stringDelimiter && !isEscaped) {
numConsecutiveStringDelimiters += 1;
}
else if (checkNextCharForString) {
numConsecutiveStringDelimiters = 0;
stringDelimiter = null;
}
else {
numConsecutiveStringDelimiters = 0;
}
checkNextCharForString = false;
// If stringDelimiter is set, then we are in a string
// Note that this works correctly even for triple quoted strings
if (stringDelimiter) {
if (isEscaped) {
// If current character is escaped, then we do not care what it was,
// but since it is impossible for the next character to be escaped as well,
// go ahead and set that to false
isEscaped = false;
}
else if (c === stringDelimiter) {
// We are seeing the same quote that started the string, i.e. ' or "
if (inTripleQuotedString) {
if (numConsecutiveStringDelimiters === 3) {
// Breaking out of the triple quoted string...
numConsecutiveStringDelimiters = 0;
stringDelimiter = null;
inTripleQuotedString = false;
}
}
else if (numConsecutiveStringDelimiters === 3) {
// reset the count, correctly handles cases like ''''''
numConsecutiveStringDelimiters = 0;
inTripleQuotedString = true;
}
else if (numConsecutiveStringDelimiters === 2) {
// We are not currently in a triple quoted string, and we've
// seen two of the same string delimiter in a row. This could
// either be an empty string, i.e. '' or "", or it could be
// the start of a triple quoted string. We will check the next
// character, and if it matches then we know we're in a triple
// quoted string, and if it does not match we know we're not
// in a string any more (i.e. it was the empty string).
checkNextCharForString = true;
}
else if (numConsecutiveStringDelimiters === 1) {
// We are not in a string that is not triple quoted, and we've
// just seen an un-escaped instance of that string delimiter.
// In other words, we've left the string.
// It is also worth noting that it is impossible for
// numConsecutiveStringDelimiters to be 0 at this point, so
// this set of if/else if statements covers all cases.
stringDelimiter = null;
}
}
else if (c === "\\") {
// We are seeing an unescaped backslash, the next character is escaped.
// Note that this is not exactly true in raw strings, HOWEVER, in raw
// strings you can still escape the quote mark by using a backslash.
// Since that's all we really care about as far as escaped characters
// go, we can assume we are now escaping the next character.
isEscaped = true;
}
}
else if ("[({".includes(c)) {
// If the only characters after this opening bracket are whitespace,
// then we should do a hanging indent. If there are other non-whitespace
// characters after this, then they will set the canHang boolean to false
canHang = true;
openBracketStack.push([row, col]);
}
else if (" \t\r\n".includes(c)) { // just in case there's a new line
// If it's whitespace, we don't care at all
}
else if (c === "#") {
break; // skip the rest of this line.
}
else {
// We've already skipped if the character was white-space, an opening
// bracket, or a comment, so that means the current character is not
// whitespace and not an opening bracket, so canHang needs to get set to
// false.
canHang = false;
// Similar to above, we've already skipped all irrelevant characters,
// so if we saw a colon earlier in this line, then we would have
// incorrectly thought it was the end of a def/for/if/elif/else/try/except
// block when it was actually a dictionary being defined/type hinting,
// reset the lastColonRow variable to whatever it was when we started
// parsing this line.
lastColonRow = lastlastColonRow;
if (c === ":") {
lastColonRow = row;
}
else if ("})]".includes(c) && openBracketStack.length) {
const openedRow = openBracketStack.pop()[0];
// lastClosedRow is used to set the indentation back to what it was
// on the line when the corresponding bracket was opened. However,
// if the bracket was opened on this same line, then we do not need
// or want to do that, and in fact, it can obscure other earlier
// bracket pairs. E.g.:
// def f(api):
// (api
// .doSomething()
// .anotherThing()
// ).finish()
// print('Correctly indented!')
// without the following check, the print statement would be indented
// 5 spaces instead of 4.
if (row !== openedRow) {
lastClosedRow = [openedRow, row];
}
}
else if ("'\"".includes(c)) {
// Starting a string, keep track of what quote was used to start it.
stringDelimiter = c;
numConsecutiveStringDelimiters += 1;
}
}
}
}
return {
canHang, dedentNext, lastClosedRow, lastColonRow, openBracketStack, lastSeenIndenters: lastSeenIndents
};
}
function indentationInfo(lines, tabSize) {
const parseOutput = parseLines(lines);
const nextIndent = nextIndentationLevel(parseOutput, lines, tabSize);
return { nextIndentationLevel: nextIndent, parseOutput };
}
exports.indentationInfo = indentationInfo;
// Determines if a hanging indent should happen, and if so how much of one
function shouldHang(line, char) {
if (char <= 0 || line.length === 0) {
return Hanging.none;
}
// Line continuation using backslash
if (line[char - 1] === "\\") {
return Hanging.partial;
}
if (!"[({".includes(line[char - 1])) {
return Hanging.none;
}
// These characters don't have an effect one way or another.
const neutralChars = ": \t\r".split("");
// The presence of these characters mean that we're in Full mode.
const fullChars = "]})".split("");
const theRest = new Set(line.slice(char).split(""));
// We only return Hanging.Full if the rest of the characters
// are neutralChars/fullChars, *and* if at least one of the fullChars
// is in theRest
neutralChars.forEach((c) => theRest.delete(c));
const containsSomeChars = theRest.size > 0;
fullChars.forEach((c) => theRest.delete(c));
const containsOnlyFullChars = theRest.size === 0;
if (containsSomeChars && containsOnlyFullChars) {
return Hanging.full;
}
return Hanging.partial;
}
exports.shouldHang = shouldHang;
//# sourceMappingURL=parser.js.map

View File

@ -0,0 +1,123 @@
{
"name": "vsc-python-indent",
"displayName": "Python Indent",
"description": "Correct Python indentation",
"version": "1.18.0",
"engines": {
"vscode": "^1.65.0"
},
"repository": {
"type": "git",
"url": "https://github.com/kbrose/vsc-python-indent"
},
"publisher": "KevinRose",
"license": "MIT",
"categories": [
"Formatters",
"Keymaps",
"Programming Languages"
],
"keywords": [
"python",
"indent",
"dedent",
"indentation",
"whitespace"
],
"qna": false,
"icon": "static/logo.png",
"activationEvents": [
"onLanguage:python",
"onLanguage:jupyter",
"onDebugResolve:python",
"onCommand:pythonIndent.newlineAndIndent"
],
"main": "./out/extension.js",
"contributes": {
"keybindings": [
{
"command": "pythonIndent.newlineAndIndent",
"key": "enter",
"when": "editorTextFocus && !editorHasMultipleSelections && editorLangId == python && !suggestWidgetVisible && !vim.active && !neovim.mode"
},
{
"command": "pythonIndent.newlineAndIndent",
"key": "enter",
"when": "editorTextFocus && !editorHasMultipleSelections && editorLangId == python && !suggestWidgetVisible && vim.active == true && vim.mode =~ /(Insert|Replace|SurroundInputMode)/"
},
{
"command": "pythonIndent.newlineAndIndent",
"key": "enter",
"when": "editorTextFocus && !editorHasMultipleSelections && editorLangId == python && !suggestWidgetVisible && neovim.mode == insert"
}
],
"commands": [
{
"command": "pythonIndent.newlineAndIndent",
"title": "newline and auto indent"
}
],
"configuration": {
"type": "object",
"title": "Python Indent configuration",
"properties": {
"pythonIndent.useTabOnHangingIndent": {
"type": "boolean",
"default": false,
"description": "After creating a hanging indent, press tab to leave the indented section and go to the ending bracket."
},
"pythonIndent.trimLinesWithOnlyWhitespace": {
"type": "boolean",
"default": false,
"description": "Trims lines that contain only whitespace after pressing Enter on them."
},
"pythonIndent.keepHangingBracketOnLine": {
"type": "boolean",
"default": false,
"description": "When creating a hanging indent, do not put the closing bracket on its own line."
}
}
}
},
"extensionKind": [
"ui",
"workspace"
],
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"lint": "eslint -c .eslintrc.js --ext .ts src/",
"pretest": "npm run compile",
"test": "node ./out/test/runTests.js"
},
"devDependencies": {
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.0",
"@types/node": "^16",
"@types/vscode": "^1.65",
"@typescript-eslint/eslint-plugin": "^5.16.0",
"@typescript-eslint/parser": "^5.16.0",
"@vscode/test-electron": "^2.1.3",
"eslint": "^8.12.0",
"glob": "^7.2.0",
"mocha": "^9.1.0",
"typescript": "^4.6.3"
},
"dependencies": {
"python-indent-parser": "0.1.4"
},
"__metadata": {
"id": "f3cbfb84-b1e1-40ff-b70f-877253461260",
"publisherId": "23d4ba77-bbea-4ea1-94ee-996a1a497aba",
"publisherDisplayName": "Kevin Rose",
"targetPlatform": "undefined",
"isApplicationScoped": false,
"isPreReleaseVersion": false,
"hasPreReleaseVersion": false,
"installedTimestamp": 1718224747285,
"pinned": false,
"preRelease": false,
"source": "gallery"
}
}

View File

@ -0,0 +1,16 @@
data = {'a': 0,
'b': [[1, 2,],
[3, 4]],
'c': 5}
def hello(
first: bool, second: bool,
):
# This comment line is waaaaaaaaaaaay too long.
if first and second:
raise ValueError('no')
elif first:
print('hello')
elif second:
print('world')
return 'done'

View File

@ -0,0 +1,42 @@
tell application "System Events"
# left : 123
# right : 124
# down : 125
# up : 126
delay 4
set textBuffer to "data = {'a': 0,
'b': [[1, 2,>,
[3, 4>>,
'c': 5>
def hello(>:<<
first: bool, second: bool^
if first and second:
raise ValueError('no')
elif first:
print('hello')
elif second:
print('world')
$return 'done'
"
repeat with i from 1 to count characters of textBuffer
if (character i of textBuffer = "<") then
key code 123
else if (character i of textBuffer = ">") then
key code 124
else if (character i of textBuffer = "^") then
key code 125
delay 0.25
else if (character i of textBuffer = "
") then
keystroke return
delay 0.25
else if (character i of textBuffer = "$") then
keystroke tab using shift down
delay 0.2
else
keystroke (character i of textBuffer)
end if
delay 0.08
end repeat
end tell

Binary file not shown.

After

Width:  |  Height:  |  Size: 821 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "src",
/* Strict Type-Checking Option */
"strict": true, /* enable all strict type-checking options */
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"exclude": [
"node_modules",
".vscode-test"
]
}

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<Identity Language="en-US" Id="rainbow-csv" Version="3.12.0" Publisher="mechatroner" />
<DisplayName>Rainbow CSV</DisplayName>
<Description xml:space="preserve">Highlight CSV and TSV files, Run SQL-like queries</Description>
<Tags>csv,tsv,highlight,CSV,__ext_csv,TSV,__ext_tsv,__ext_tab,csv (pipe),CSV (pipe),csv (whitespace),CSV (whitespace),csv (semicolon),CSV (semicolon),dynamic csv,Dynamic CSV,plaintext,sql,__web_extension</Tags>
<Categories>Data Science,Other,Programming Languages</Categories>
<GalleryFlags>Public</GalleryFlags>
<Properties>
<Property Id="Microsoft.VisualStudio.Code.Engine" Value="^1.71.0" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionDependencies" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionPack" Value="" />
<Property Id="Microsoft.VisualStudio.Code.ExtensionKind" Value="workspace,web" />
<Property Id="Microsoft.VisualStudio.Code.LocalizedLanguages" Value="" />
<Property Id="Microsoft.VisualStudio.Services.Links.Source" Value="https://github.com/mechatroner/vscode_rainbow_csv.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Getstarted" Value="https://github.com/mechatroner/vscode_rainbow_csv.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.GitHub" Value="https://github.com/mechatroner/vscode_rainbow_csv.git" />
<Property Id="Microsoft.VisualStudio.Services.Links.Support" Value="https://github.com/mechatroner/vscode_rainbow_csv/issues" />
<Property Id="Microsoft.VisualStudio.Services.Links.Learn" Value="https://github.com/mechatroner/vscode_rainbow_csv#readme" />
<Property Id="Microsoft.VisualStudio.Services.GitHubFlavoredMarkdown" Value="true" />
</Properties>
<License>extension/LICENSE.txt</License>
<Icon>extension/rainbow_csv_logo.png</Icon>
</Metadata>
<Installation>
<InstallationTarget Id="Microsoft.VisualStudio.Code"/>
</Installation>
<Dependencies/>
<Assets>
<Asset Type="Microsoft.VisualStudio.Code.Manifest" Path="extension/package.json" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Details" Path="extension/README.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.Changelog" Path="extension/CHANGELOG.md" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Content.License" Path="extension/LICENSE.txt" Addressable="true" />
<Asset Type="Microsoft.VisualStudio.Services.Icons.Default" Path="extension/rainbow_csv_logo.png" Addressable="true" />
</Assets>
</PackageManifest>

View File

@ -0,0 +1,169 @@
# Rainbow CSV for Visual Studio Code Change Log
## 3.12.0
* Improved Dynamic CSV separator selection UI which allows more (separator, policy) combinations
* Minor fixes
## 3.11.0
* Add user-friendly sticky header config option.
* Enable sticky header by default.
* Fix tooltip hover text bug, see [#157](https://github.com/mechatroner/vscode_rainbow_csv/issues/157).
## 3.10.0
* Fix major dynamic csv semantic highlighting bug for many non-built-in color themes [#149](https://github.com/mechatroner/vscode_rainbow_csv/issues/149)
* Fix comment lines highlighting for many non-built-in color themes
* Make dynamic csv semantic highlighting colors consistent with regular texmate grammar colors [#149](https://github.com/mechatroner/vscode_rainbow_csv/issues/149)
* RBQL: More robust python invocation order, see [#148](https://github.com/mechatroner/vscode_rainbow_csv/issues/148)
* Add debug logging option to facilitate bug reporting and triaging.
## 3.9.0
* Adjust state transition logic and improve UX for some edge cases.
* Get rid of rainbow hover text colorizing since it probably didn't work anyway.
## 3.8.0
* UI improvement: Better "CSVLint" status button by [@kostasx](https://github.com/kostasx).
* Add "Rainbow ON" conditional button.
* Update RBQL: Add `ANY_VALUE` aggregate function.
* Other minor improvements.
## 3.7.1
* Update RBQL: Fix GROUP BY queries with bare columns, see [#144](https://github.com/mechatroner/vscode_rainbow_csv/issues/144)
## 3.7.0
* Add config option to align in a new file, see [#62](https://github.com/mechatroner/vscode_rainbow_csv/issues/62).
* Other minor fixes and improvements.
## 3.6.0
* Improve CSV alignment for files containing double-width characters e.g. Chinese or Japanese characters.
* Fix performance bug in CSV alignment procedure - alignment should work noticeable faster for large files.
## 3.5.1
* Minor bugfix by lramos15@.
## 3.5.0
* Add Fixed Sticky Header support. Proposed by @BeneKenobi, see [#124](https://github.com/mechatroner/vscode_rainbow_csv/issues/124).
* Minor fixes.
## 3.4.0
* Fix minor interoperability issue with other extensions (additional autodetection check, see [#123](https://github.com/mechatroner/vscode_rainbow_csv/issues/123)).
* Update RBQL: support `AS` column alias in queries.
## 3.3.0
* Support column alignment for CSV files with multiline fields (rfc-4180).
* Remove uncommon csv dialects (such as tilde, colon and other separators) in favor of generic "dynamic csv".
* Update docs.
## 3.2.0
* UX improvements for Dynamic CSV filetype.
## 3.1.0
* Support comment lines toggle, see [#84](https://github.com/mechatroner/vscode_rainbow_csv/issues/84).
* Support double quote autoclosing and text auto-surrounding.
* Minor UX improvements
* Minor Bug fixes
## 3.0.0
* Support infinite number of arbitrary single-character and multi-character separators.
* Support multiline fields with RFC-4180 - compatible syntax highlighting.
* Support highlighting of comment lines.
* Various minor usability improvements and fixes.
* Update RBQL.
## 2.4.0
* Show cursor column info in the status line.
* UI and UX improvements.
## 2.3.0
* Improve alignment algorithm: special handling of numeric columns, see [#106](https://github.com/mechatroner/vscode_rainbow_csv/issues/106).
* Show alignment progress indicator which is very nice for large files.
## 2.2.0
* UI and UX improvements by [@anthroid](https://github.com/anthroid).
## 2.1.0
* Support RBQL and column edit mode in web version of VSCode.
* Support RBQL result set output dir customization [#101](https://github.com/mechatroner/vscode_rainbow_csv/issues/101).
* Slightly reduce startup time by moving non-critical code into a lazy-loaded module.
* Internal code refactoring.
## 2.0.0
* Enable web/browser version for vscode.dev
* RBQL: improve join table path handling.
## 1.10.0
* RBQL update: improved console UI.
## 1.9.0
* RBQL update: improved CSV header support.
## 1.8.1
* Minor RBQL update
## 1.8.0
* New command: "SetHeaderLine" by @WetDesertRock, see [#71](https://github.com/mechatroner/vscode_rainbow_csv/issues/71)
* Updated RBQL
* Added integration tests
## 1.7.0
* Updated RBQL
* Improved RBQL UI
## 1.6.0
* Updated RBQL
## 1.5.0
* Highlight column info tooltip with the same color as the column itself
## 1.4.0
* Run CSV autodetection whenever a text chunk is copied into a new untitled buffer
* Improve startup performance
* RBQL: Support column names as variables
* RBQL: Support newlines in double-quoted CSV fields
* RBQL: Change default encoding to UTF-8
* RBQL: Enable for untitled buffers
* RBQL: Improve UI/UX, add history, built-in docs
## 1.3.0
* Updated RBQL to version 0.9.0
* Restricted usage of Align/Shrink commands in files with unbalanced double quotes
* Fixed incorrect dialect name: "tilda" -> "tilde", see [#40](https://github.com/mechatroner/vscode_rainbow_csv/issues/40)
* Added an eror message when RBQL console is used with unsaved file [#41](https://github.com/mechatroner/vscode_rainbow_csv/issues/41)
## 1.2.0
* Added frequency-based fallback content-based autodetection algorithm for .csv files
* Adjusted default parameters: added '|' to the list of autodetected separators
* Fixed "Align/Shrink" button logic [#38](https://github.com/mechatroner/vscode_rainbow_csv/issues/38)
* Fixed bug: incorrect RBQL result set dialect when output dialect doesn't match input
* Improved documentation
## 1.1.0
* Special treatment of comment lines by [@larsonmars](https://github.com/larsonmars)
* RBQL encoding customization by [@mandel59](https://github.com/mandel59)
* Implemented Whitespace-separated dialect
* Linter: detect trailing whitespaces in fields [#15](https://github.com/mechatroner/vscode_rainbow_csv/issues/15)
* Added commands: remove trailing whitespaces from all fields and allign columns with trailing whitespaces
* Implemented RBQL result set copy-back command
* Improved RBQL console UI
* Customizable "Preview big CSV: head/tail" context menu options [#32](https://github.com/mechatroner/vscode_rainbow_csv/issues/32)
* Improved autodetection algorithm for files with multiple candidate separators
## 0.8.0
* Large files preview functionality implemented by [@neilsustc](https://github.com/neilsustc) see [#24](https://github.com/mechatroner/vscode_rainbow_csv/issues/24)
* Fix single-autodetection per file limit, see [#26](https://github.com/mechatroner/vscode_rainbow_csv/issues/26)
* Enable content-based autodetection for .csv files
* Support tooltip message customizations, see [#12](https://github.com/mechatroner/vscode_rainbow_csv/issues/12)
* Fix RBQL warnings
* Various minor improvements
## 0.7.1
* Fix: Added safety check to RBQL that would prevent accidental usage of assignment operator "=" instead of comparison "==" or "===" in JS (In Python this was not possible before the fix due to Python's own syntatic checker).
* Added "Rainbow CSV" category to all extension commands by [@yozlet](https://github.com/yozlet) request, see [#22](https://github.com/mechatroner/vscode_rainbow_csv/issues/22)

View File

@ -0,0 +1,168 @@
## Instructions
### Key Info
* Surprising VSCode filetype (language mode in the bottom-right corner) persistence behaviour:
- filetypes are often (or always?) preserved across restarts, even "Dynamic CSV".
- filetypes are not preserved on file re-open (both non-preview and preview modes)
- sometimes filetype are not preserved across tab back-and-forth switching (let's call this "curious doc reopening" problem)
this is reproducible when first selecting a manual separator e.g. `~#~`, then switching to "plain text" through the lang selection menu, than back to "dynamic csv".
Now "Dynamic CSV" and highlighting would disappear if user clicks another tab and back - the "dynamic csv" version of the doc would be closed and plaintext doc opened.
It is possible to "fix" this scenario for a particular doc by explicitly closing and opening it again.
Apparently this also affects the reverse situation and switch from plaintext back to e.g. TSV when we select TSV manually, then click "Rainbow Off", switch tabs and it is "On" again. So somehow manual "language mode" selection is more persistent than setTextDocumentLanguage effect.
* "Dynamic CSV" highlighting might not work because of the conflict with some other extensions.
* To ensure the required behaviour we will use "defensive programming" paradigm and there might be some redundancy in the method invocation (e.g. we might disable/enable something twice both on doc opening and closing), this is fine as long as the invocations are idempotent.
### Pre-publishing checklist
* Make sure to run unit tests in browser.
* Make sure that "sticky header" feature works if enabled.
* Make sure that "Dynamic CSV" -> "Dynamic CSV" switch from one separator to another works.
* Run `npm run lint`
### Debugging the extension:
#### For standard VSCode:
1. Open rainbow_csv directory in VSCode
2. Make sure you have "Extension" run mode enabled
3. Click "Run" or F5
#### For web-based VSCode:
1. Run `npm install` - OK to run in WSL.
2. Run `npm run compile-web && npm run start-web-server` - OK to run in WSL. - This should start a local server at http://localhost:3000/
3. Point your browser to http://localhost:3000/
It is possible to run this in windows cmd too, but it could be that the node_modules dir has to be deleted and installed from scratch.
The difference between running in win and in WSL is that in WSL it would only run with `--browser=none` option and this option doesn't run unit tests automatically which could be an issue if you want to run tests instead of manual debugging.
NOTE: if `npm run test-in-browser` fails with an error it could show a message telling to install/update playwright - do it (it will also print the command, something like: `npx playwright install`).
### Running unit tests for the extension OUTSIDE VSCode:
#### For standard VSCode:
1. **IMPORTANT** Make sure you have no open VSCode instances running, all VSCode windows are closed (otherwise will might get some weird caching/webworker errors or other issues)!
2. run `npm install` (If you have WSL - run in Windows, don't run in WSL).
3. run `npm run test` in Windows (If you have WSL - run in Windows, don't run in WSL). Make sure that the tests are successful.
#### For web-based VSCode:
1. run `npm install` (If you have WSL - run in Windows, don't run in WSL).
2. run `npm run compile-web` (If you have WSL - run in Windows, don't run in WSL). This will combine all scripts into a single web script and put it into the `dist` folder.
3. run `npm run test-in-browser` (If you have WSL - run in Windows, don't run in WSL). This will open a new browser windows and run all the unit tests. Make sure that the tests are successful.
NOTE: if `npm run test-in-browser` fails with an error it could show a message telling to install/update playwright - do it (it will also print the command, something like: `npx playwright install`).
### Running unit tests for the extension INSIDE VSCode:
1. In console in rainbow_csv directory run `npm install` - OK to run the command in WSL while launching in Windows. This will install the dependencies, including `vscode/lib/testrunner`
2. Open rainbow_csv directory in VSCode switch to "Extension Tests" mode and click run
**IMPORTANT** for some reason unit tests that are run from inside VSCode will often start with some files reopened from the previous session, this among other issues could cause `is_lazy_loaded` check to fail. An only known workaround is to quickly close all tabs in the test run and then restart the tests.
Example of minimalistic test setup:
https://github.com/microsoft/vscode-extension-samples/tree/main/helloworld-test-sample
#### Debuging
Looks like it is possible to directly run scripts from package.json with `npx` like this:
```
npx vscode-test-web --help
```
And apparently another option to execute this command is (never tested):
```
npx @vscode/test-web --extensionDevelopmentPath=$extensionFolderPath $testDataPath
```
Options available for vscode-test-web
* version
`'insiders' | 'stable' | 'sources' [Optional, default 'insiders']`
* browser
`'chromium' | 'firefox' | 'webkit' | 'none': The browser to launch. [Optional, defaults to 'chromium']`
If `none` is provided it wouldn't run unit test and it wouldn't kill the server when the browser window is closed.
If exceptions happens in extension.js you will be able to see it in the browser console (but the line number would be wrong).
#### Issues
* FS mount not working: https://github.com/microsoft/vscode-test-web/issues/16
### Running the browser version for vscode.dev
The npx command `npx serve --cors -l 5000` failed in WSL with `cb.apply is not a function` error.
The same command worked as expected from Windows cmd.
Steps:
1. Run `npx serve --cors -l 5000` - this may not work in WSL, in this case run in windows cmd. This local server uses `http` instead of `https` and because of that VSCode will not work with it directly, although the docs say otherwise (https://code.visualstudio.com/api/extension-guides/web-extensions#test-your-web-extension-in-on-vscode.dev) - it will just produce some cors/wss content security policy related errors in the log. So you need to do step 2.
2. In another cmd tab run another commmand: `npx localtunnel -p 5000` - this will create a "tunnel" server pointing to the server from the first command - this will produce a link like `https://rotten-snake-42.loca.lt/`
3. Follow the `https://rotten-snake-42.loca.lt/` link and press the button - this will show the content of your extension folder - https server is working.
4. Go to vscode.dev -> Ctrl+Shift+P -> run Developer: Install Web Extension... -> Copy the `https://rotten-snake-42.loca.lt/` link. In my experience this will work only with https urls. If you use http, the extension will be sort of "installed" - it will be listed in the installed extension pannel but the main extension.js won't be loaded so all the logic will be missing from it.
### Publishing
1. Make sure you have webpack installed: run `npm install` (Better to avoid running this in WSL).
Although this would create `node_modules/` and `package-lock.json` file this is not a problem because they are excluded from final package via the `.vscodeignore` file.
2. Run `vsce publish minor -p <key>` as usual. vsce will also automatically run `vscode:prepublish` / `npm run package-web` command.
### Publishing to openvsx
1. Make sure you have webpack installed: run `npm install` (Better to avoid running this in WSL).
Although this would create `node_modules/` and `package-lock.json` file this is not a problem because they are excluded from final package via the `.vscodeignore` file.
2. Run `npx ovsx publish -p <openvsx_token>`
Unlike vsce publishing for the official Microsoft VSCode marketplace, ovsx tool does not need to be provided with version increment specification (such as major/minor/fix), instead it will just use the version from package.json file which is very convenient. And it will also won't try to update that version value in package.json during the publishing process.
See more docs here: https://github.com/eclipse/openvsx/wiki/Publishing-Extensions
### Testing for remote development
1. Install code on the remote machine. Note: In my case "snap" installation didn't work, but the deb package worked without issues.
2. Run `code tunnel`, see more here: https://code.visualstudio.com/docs/remote/tunnels#_using-the-code-cli
3. Access the link from the browser or through the desktop VSCode app using `Remote - Tunnels` extension https://code.visualstudio.com/blogs/2022/12/07/remote-even-better
Enter `Remote - Tunnels: Connect to Tunnel` command in the pallete.
### Generating documentation with showdown
In order to generate RBQL documentation use showdown - based markdown_to_html.js script from junk/rainbow_stuff
Usage: `node markdown_to_html.js ~/vscode_rainbow_csv/rbql_core/README.md out.html`
## Other
### TODO LIST
* Improve RBQL encoding handling logic when VScode encoding info API is implemented, see https://github.com/microsoft/vscode/issues/824.
* Consider keeping only one open RBQL console at any time - if another one opens automatically close the previous one.
* DEBUG: Add a huge no-op loop on startup in order to reproduce/emulate high-cpu load error from #55.
* Support virtual header for rbql_csv.
* Consider replacing the RBQL query text input with scrollable textarea - it has a drawback that on enter it will go to the next line instead running the query.
* Store VSCode documents instead of file paths in result_set_parent_map so that the map can be used in web version. And the autodetection_stoplist also should be doc based to work in web.
* Support JOIN queries in web version.
* Support all commands in web version
* Get rid of `then` entirely
* Merge rbql_query_web and rbql_query_node
* Add feature to decorate separators with a transparent box or different color or something, see the opened issue.
* Consider using RFC-like syntax by ajhyndman, see https://github.com/mechatroner/vscode_rainbow_csv/issues/4
* Consider removing double quote autoclosing from non-csv/scsv dialects when native rfc csv is enabled.
* Consider adding optional "keep-it-dark" mode that would force dark color theme even if all other filetypes use light color theme to improve csv readability.
* Add `"csv (tab)"` alias to TSV language if/when https://github.com/microsoft/vscode/issues/167208 is resolved
### RFC Support plan
1. Full doc tokenization (rbql + rbql UI preview sampling + lint + autodetection) - 100% correct, doesn't have to use VSCode ranges.
2. Fragment tokenization (hover + highlighting) - best effort, fast, should use VSCode ranges.
### Maximum supported file size in VSCode
https://stackoverflow.com/questions/53625687/what-is-the-largest-filesize-supported-by-vs-code-syntax-highlighting
20MB Or 300K lines (avg 66 chars per line).

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Dmitry Ignatovich
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More