├── .no-sublime-package
├── .gitignore
├── images
├── logo.png
├── elm_format.png
├── elm_make.jpg
├── elm_repl.jpg
├── elm_types.png
├── type_panel.png
├── completions.png
└── elm_project.jpg
├── Settings
├── Elm.sublime-settings
├── Elm Language Support.sublime-settings
└── Elm User Strings.sublime-settings
├── messages
├── 0.17.0.md
├── 0.16.2.md
├── 0.19.0.txt
├── 0.17.1.txt
├── 0.15.0.md
├── 0.18.0.txt
├── 0.16.1.md
└── 0.16.3.md
├── Commands
├── Elm Format.sublime-commands
├── Open In Browser.sublime-commands
├── Show Type.sublime-commands
├── REPL.sublime-commands
└── Project.sublime-commands
├── Snippets
├── module.sublime-snippet
├── type.sublime-snippet
├── import.sublime-snippet
├── let.sublime-snippet
├── import_as.sublime-snippet
├── case_of.sublime-snippet
├── function.sublime-snippet
├── type_alias.sublime-snippet
├── case_of_maybe.sublime-snippet
├── case_of_result.sublime-snippet
├── function_2_aguments.sublime-snippet
├── function_3_aguments.sublime-snippet
└── function_4_aguments.sublime-snippet
├── Keymaps
└── Default.sublime-keymap
├── Menus
├── Context.sublime-menu
└── Main.sublime-menu
├── messages.json
├── beta-repository.json
├── Syntaxes
├── Elm Documentation.YAML-tmLanguage
├── Elm Documentation.hidden-tmLanguage
├── Elm Documentation.hide-tmLanguage
├── Elm Compile Messages.YAML-tmLanguage
├── Elm Compile Messages.hidden-tmLanguage
├── Elm.YAML-tmLanguage
└── Elm.tmLanguage
├── Preferences
├── Metadata.YAML-tmLanguage
└── Metadata.tmPreferences
├── LICENSE
├── elm_open_in_browser.py
├── Build Systems
└── Elm Make.sublime-build
├── elm_format.py
├── elm_plugin.py
├── elm_make.py
├── README.md
├── elm_generate.py
├── elm_project.py
└── elm_show_type.py
/.no-sublime-package:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.cache
2 | *.pyc
3 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/logo.png
--------------------------------------------------------------------------------
/images/elm_format.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/elm_format.png
--------------------------------------------------------------------------------
/images/elm_make.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/elm_make.jpg
--------------------------------------------------------------------------------
/images/elm_repl.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/elm_repl.jpg
--------------------------------------------------------------------------------
/images/elm_types.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/elm_types.png
--------------------------------------------------------------------------------
/images/type_panel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/type_panel.png
--------------------------------------------------------------------------------
/images/completions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/completions.png
--------------------------------------------------------------------------------
/images/elm_project.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm-community/Elm.tmLanguage/master/images/elm_project.jpg
--------------------------------------------------------------------------------
/Settings/Elm.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | "word_separators": "/\\()\"-:,.;<>!@#$%^&*|+=[]{}`~?",
3 | "translate_tabs_to_spaces": true
4 | }
5 |
--------------------------------------------------------------------------------
/messages/0.17.0.md:
--------------------------------------------------------------------------------
1 | ## What's new
2 |
3 | - Added support for new Elm 0.17 module declaration syntax.
4 | - Package adopted by elm-community.
5 |
--------------------------------------------------------------------------------
/Commands/Elm Format.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Elm Language Support: Run elm-format",
4 | "command": "elm_format"
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/Commands/Open In Browser.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Elm Build System: Open in Browser",
4 | "command": "elm_open_in_browser"
5 | }
6 | ]
7 |
--------------------------------------------------------------------------------
/messages/0.16.2.md:
--------------------------------------------------------------------------------
1 | ## What's new
2 |
3 | This is just a small update to fix an issue where the Elm Make command was no longer compatible with the elm-make command line utility on Windows. This update will not change anything on Linux or Mac.
4 |
--------------------------------------------------------------------------------
/Snippets/module.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | module
7 | mod
8 | source.elm
9 |
10 |
--------------------------------------------------------------------------------
/Snippets/type.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | type
9 | type
10 | source.elm
11 |
12 |
--------------------------------------------------------------------------------
/Keymaps/Default.sublime-keymap:
--------------------------------------------------------------------------------
1 | [
2 | { "keys": ["alt+up"], "command": "elm_show_type_panel",
3 | "context":
4 | [ { "key": "selector", "operator": "equal", "operand": "source.elm" } ]
5 | },
6 | { "keys": ["alt+down"], "command": "hide_panel"
7 | }
8 | ]
--------------------------------------------------------------------------------
/Snippets/import.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | import
7 | imp
8 | source.elm
9 |
10 |
--------------------------------------------------------------------------------
/Snippets/let.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 | let … in …
11 | let
12 | source.elm
13 |
14 |
--------------------------------------------------------------------------------
/Snippets/import_as.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | import … as
7 | impas
8 | source.elm
9 |
10 |
--------------------------------------------------------------------------------
/Snippets/case_of.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 | $3
6 | ${4:_} ->
7 | $5
8 | ]]>
9 |
10 | case … of
11 | cof
12 | source.elm
13 |
14 |
--------------------------------------------------------------------------------
/messages/0.19.0.txt:
--------------------------------------------------------------------------------
1 | New in Elm Language Support 0.19.0 (May 28, 2017)
2 |
3 | - Improvement: Side-by-side editing of package user settings (requires Sublime
4 | Text 3 Build 3124). (Thanks, @stoivo!)
5 |
6 | - Fix: Blank menu item when SublimeREPL is not installed. (Thanks, @Bernardoow!)
7 |
--------------------------------------------------------------------------------
/Settings/Elm Language Support.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | "debug": false,
3 | "enabled": true,
4 | "elm_docs_path": "docs.json",
5 | "elm_format_binary": "elm-format",
6 | "elm_format_on_save": true,
7 | "elm_format_filename_filter": "",
8 | "elm_paths": ""
9 | }
10 |
--------------------------------------------------------------------------------
/Snippets/function.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 | ${2:Int}
4 | ${1:fn} ${4:count} =
5 | ${5:-- body}
6 | ]]>
7 | Function (a -> b)
8 | fn
9 | source.elm
10 |
11 |
--------------------------------------------------------------------------------
/Snippets/type_alias.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 | type alias (Record)
10 | typea
11 | source.elm
12 |
13 |
--------------------------------------------------------------------------------
/Snippets/case_of_maybe.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 | $3
6 | Nothing ->
7 | $4
8 | ]]>
9 |
10 | case … of (Maybe)
11 | cofm
12 | source.elm
13 |
14 |
--------------------------------------------------------------------------------
/Snippets/case_of_result.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 |
5 | $3
6 | Error ${4:error} ->
7 | $5
8 | ]]>
9 |
10 | case … of (Result)
11 | cofr
12 | source.elm
13 |
14 |
--------------------------------------------------------------------------------
/Commands/Show Type.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Elm Language Support: Show type",
4 | "command": "elm_show_type",
5 | "args": { "panel": true }
6 | },
7 | {
8 | "caption": "Elm Language Support: Open type panel",
9 | "command": "elm_show_type_panel"
10 | }
11 | ]
--------------------------------------------------------------------------------
/Snippets/function_2_aguments.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 | ${4:Int} -> ${2:Int}
4 | ${1:fn} ${5:a} ${6:a} =
5 | ${7:-- body}
6 | ]]>
7 | Function (a -> b -> c)
8 | fn2
9 | source.elm
10 |
11 |
--------------------------------------------------------------------------------
/Menus/Context.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "elmlanguagesupport",
4 | "caption": "Elm Language Support",
5 | "children":
6 | [
7 | {
8 | "caption": "Open Type Panel",
9 | "command": "elm_show_type_panel"
10 | }
11 | ]
12 | }
13 | ]
--------------------------------------------------------------------------------
/messages.json:
--------------------------------------------------------------------------------
1 | {
2 | "install": "README.md",
3 | "0.15.0": "messages/0.15.0.md",
4 | "0.16.1": "messages/0.16.1.md",
5 | "0.16.2": "messages/0.16.2.md",
6 | "0.16.3": "messages/0.16.3.md",
7 | "0.17.0": "messages/0.17.0.md",
8 | "0.17.1": "messages/0.17.1.txt",
9 | "0.18.0": "messages/0.18.0.txt"
10 | }
11 |
--------------------------------------------------------------------------------
/Snippets/function_3_aguments.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 | ${4:Int} -> ${5:Int} -> ${2:Int}
4 | ${1:fn} ${6:a} ${7:b} ${8:c} =
5 | ${9:-- body}
6 | ]]>
7 | Function (a -> b -> c -> d)
8 | fn3
9 | source.elm
10 |
11 |
--------------------------------------------------------------------------------
/Snippets/function_4_aguments.sublime-snippet:
--------------------------------------------------------------------------------
1 |
2 | ${4:Int} -> ${5:Int} -> ${6:Int} -> ${2:Int}
4 | ${1:fn} ${7:a} ${8:b} ${9:c} ${10:d}=
5 | ${11:-- body}
6 | ]]>
7 | Function (a -> b -> c -> d -> e)
8 | fn4
9 | source.elm
10 |
11 |
--------------------------------------------------------------------------------
/beta-repository.json:
--------------------------------------------------------------------------------
1 | {
2 | "schema_version": "3.0.0",
3 | "packages": [
4 | {
5 | "name": "Elm Language Support",
6 | "details": "https://github.com/deadfoxygrandpa/Elm.tmLanguage",
7 | "releases": [
8 | {
9 | "sublime_text": "*",
10 | "branch": "beta"
11 | }
12 | ]
13 | }
14 | ],
15 | "dependencies": [
16 | ],
17 | "includes": [
18 | ]
19 | }
--------------------------------------------------------------------------------
/messages/0.17.1.txt:
--------------------------------------------------------------------------------
1 | New in Elm Language Support 0.17.1 (January 19, 2017)
2 |
3 | - New: New build output format, with support for new inline build error
4 | indicators (phantoms) introduced in Sublime Text 3 build 3118
5 | (see: https://forum.sublimetext.com/t/dev-build-3118/21270).
6 | - Fixed: F4/Shift-F4 navigation for build errors.
7 | - Fixed: Double-click build error heading to jump to location in editor.
8 | - New maintainer: Kevin Yank (@sentience)
9 |
--------------------------------------------------------------------------------
/Syntaxes/Elm Documentation.YAML-tmLanguage:
--------------------------------------------------------------------------------
1 | # [PackageDev] target_format: plist, ext: hidden-tmLanguage
2 | name: Elm Documentation
3 | scopeName: text.html.mediawiki.elm-documentation
4 | fileTypes: []
5 | uuid: c4a06e11-4259-4698-aeb2-1e87799984cc
6 |
7 | patterns:
8 | - comment: Code Block
9 | name: markup.raw.block.elm-documentation
10 | contentName: markup.raw.block.elm-documentation
11 | begin: \x{FEFF}
12 | end: \x{FEFF}
13 | patterns:
14 | - include: source.elm
15 |
--------------------------------------------------------------------------------
/messages/0.15.0.md:
--------------------------------------------------------------------------------
1 | Thank you for continuing to use Elm Language Support. We've been busy lately. Here are a few features since we last checked in:
2 |
3 | ## What's New
4 |
5 | - This message ;)
6 | - Improved ST3 compatibility
7 | - Two build commands
8 | - Compile messages
9 | - Integration with four (4!) external plugins
10 | - One new contributor — @dnalot aka "Texas"
11 | - A brand new [README][] with further details
12 |
13 | [README]: https://github.com/deadfoxygrandpa/Elm.tmLanguage/blob/master/README.md
14 |
--------------------------------------------------------------------------------
/Preferences/Metadata.YAML-tmLanguage:
--------------------------------------------------------------------------------
1 | # [PackageDev] target_format: plist, ext: tmPreferences
2 | name: Metadata
3 | uuid: 64598216-cf92-4b00-961f-2b1478921c3e
4 | scope: source.elm
5 | settings:
6 | increaseIndentPattern: |-
7 | (?x)
8 | ^.*
9 | (=
10 | |[|!%$?~+:\-.=>&\\*^]+
11 | |\bthen
12 | |\belse
13 | |\bof
14 | )
15 | \s*$
16 | shellVariables:
17 | - name: TM_COMMENT_START
18 | value: --
19 | - name: TM_COMMENT_START_2
20 | value: '{-'
21 | - name: TM_COMMENT_END_2
22 | value: -}
23 |
--------------------------------------------------------------------------------
/messages/0.18.0.txt:
--------------------------------------------------------------------------------
1 | New in Elm Language Support 0.18.0 (May 13, 2017)
2 |
3 | - New: Snippets that provide shortcuts for typing common Elm syntax: case … of,
4 | case … of (Maybe), case … of (Result), function (1-4 arguments), import,
5 | import … as, let … in, module, type, and type alias.
6 |
7 | See http://docs.sublimetext.info/en/latest/extensibility/snippets.html for
8 | more info on using snippets in Sublime Text.
9 |
10 | - Improvement: Option to specify the name of the elm-format binary, for systems
11 | where the version number is included in the filename (e.g. elm-format-0.18).
12 |
--------------------------------------------------------------------------------
/Commands/REPL.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "SublimeREPL: Elm",
4 | "command": "repl_open", "args":
5 | {
6 | "type": "subprocess",
7 | "encoding": "utf8",
8 | "cmd": ["elm-repl"],
9 | "cwd": "$file_path",
10 | "syntax": "Packages/Elm Language Support/Syntaxes/Elm.tmLanguage",
11 | "external_id": "elm",
12 | "extend_env":
13 | {
14 | "osx": {"PATH": "{PATH}:/usr/local/bin"},
15 | "linux": {"PATH": "{PATH}:/usr/local/bin:{HOME}/.cabal/bin"},
16 | "windows": {}
17 | }
18 | }
19 | }
20 | ]
21 |
--------------------------------------------------------------------------------
/Settings/Elm User Strings.sublime-settings:
--------------------------------------------------------------------------------
1 | {
2 | "logging.prefix": "[Elm says]: ",
3 | "logging.missing_plugin": "Missing plugin: {0}",
4 |
5 | "make.missing_plugin": "To highlight build errors: Install with Package Control: Highlight Build Errors",
6 | "make.logging.invalid_json": "Invalid JSON from elm-make: {0}",
7 |
8 | "open_in_browser.not_found": "HTML file NOT found to open: {0}",
9 |
10 | "project.not_found": "Valid elm-package.json NOT found to update",
11 | "project.updated": "elm-package.json updated: {0} = {1}",
12 | "project.logging.invalid_choice": "Invalid choice in elm-package.json: {0}",
13 | "project.logging.invalid_json": "Invalid elm-package.json: {0}",
14 | "project.logging.settings": "Detected settings: {0}"
15 | }
16 |
--------------------------------------------------------------------------------
/messages/0.16.1.md:
--------------------------------------------------------------------------------
1 | ## What's new
2 |
3 | Added support for elm-format, the new tool for automatically formatting your Elm source code (you need to install this separately. Check here for details: https://github.com/avh4/elm-format)
4 | - elm-format needs to be installed and in your PATH in order to work
5 | - There is a new command on the command palette named `Elm Language Support: Run elm-format`. This command will run elm-format on the currently open file.
6 | - There is also a new User setting, which you can set by going to Preferences -> Package Settings -> Elm Language Support -> User and adding `"elm_format_on_save": true`. This will automatically run elm-format on your Elm source files before they are saved.
7 | - If there are some Elm source files you want to exclude from auto-formatting, you can set a regex-based filename filter with the "elm_format_filename_filter" User setting. Enter a regex and any file names, including their paths, the regex matches against will be excluded from the "elm_format_on_save" setting previously mentioned.
8 |
--------------------------------------------------------------------------------
/Syntaxes/Elm Documentation.hidden-tmLanguage:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileTypes
6 |
7 | name
8 | Elm Documentation
9 | patterns
10 |
11 |
12 | begin
13 | \x{FEFF}
14 | comment
15 | Code Block
16 | contentName
17 | markup.raw.block.elm-documentation
18 | end
19 | \x{FEFF}
20 | name
21 | markup.raw.block.elm-documentation
22 | patterns
23 |
24 |
25 | include
26 | source.elm
27 |
28 |
29 |
30 |
31 | scopeName
32 | text.html.mediawiki.elm-documentation
33 | uuid
34 | c4a06e11-4259-4698-aeb2-1e87799984cc
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Syntaxes/Elm Documentation.hide-tmLanguage:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileTypes
6 |
7 | name
8 | Elm Documentation
9 | patterns
10 |
11 |
12 | begin
13 | \x{FEFF}
14 | comment
15 | Code Block
16 | contentName
17 | markup.raw.block.elm-documentation
18 | end
19 | \x{FEFF}
20 | name
21 | markup.raw.block.elm-documentation
22 | patterns
23 |
24 |
25 | include
26 | source.elm
27 |
28 |
29 |
30 |
31 | scopeName
32 | text.html.mediawiki.elm-documentation
33 | uuid
34 | c4a06e11-4259-4698-aeb2-1e87799984cc
35 |
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2014 Alex Neslusan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/Preferences/Metadata.tmPreferences:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | name
6 | Metadata
7 | scope
8 | source.elm
9 | settings
10 |
11 | increaseIndentPattern
12 | (?x)
13 | ^.*
14 | (=
15 | |[|!%$?~+:\-.=</>&\\*^]+
16 | |\bthen
17 | |\belse
18 | |\bof
19 | )
20 | \s*$
21 | shellVariables
22 |
23 |
24 | name
25 | TM_COMMENT_START
26 | value
27 | --
28 |
29 |
30 | name
31 | TM_COMMENT_START_2
32 | value
33 | {-
34 |
35 |
36 | name
37 | TM_COMMENT_END_2
38 | value
39 | -}
40 |
41 |
42 |
43 | uuid
44 | 64598216-cf92-4b00-961f-2b1478921c3e
45 |
46 |
47 |
--------------------------------------------------------------------------------
/elm_open_in_browser.py:
--------------------------------------------------------------------------------
1 | import webbrowser
2 |
3 | try: # ST3
4 | import urllib.parse as urlparse
5 | import urllib.request as urllib
6 |
7 | from .elm_plugin import *
8 | from .elm_project import ElmProject
9 | except: # ST2
10 | import urlparse
11 | import urllib
12 |
13 | from elm_plugin import *
14 | from elm_project import ElmProject
15 |
16 | class ElmOpenInBrowserCommand(sublime_plugin.TextCommand):
17 |
18 | def is_enabled(self):
19 | self.project = ElmProject(self.view.file_name())
20 | return self.project.exists
21 |
22 | def run(self, edit):
23 | norm_path = fs.join(self.project.working_dir, fs.expanduser(self.project.html_path))
24 | file_path = fs.abspath(norm_path)
25 | if fs.isfile(file_path):
26 | # http://stackoverflow.com/questions/11687478/convert-a-filename-to-a-file-url#comment32679033_14298190
27 | file_url = urlparse.urljoin('file:', urllib.pathname2url(file_path))
28 | # inspired by https://github.com/noahcoad/open-url
29 | webbrowser.open_new_tab(file_url)
30 | else:
31 | sublime.status_message(get_string('open_in_browser.not_found', html_path))
32 |
--------------------------------------------------------------------------------
/messages/0.16.3.md:
--------------------------------------------------------------------------------
1 | ## What's new
2 |
3 | Added support for elm-oracle to add autocompletions, type information, and in-editor documentation for external packages (you need to install this separately, npm install -g elm-oracle)
4 | - Moving the cursor over any function imported from an external package defined in your elm-package.json file will show the type signature in the status bar
5 | - Bring up the type panel with alt+up or through the right click context menu, or by running the `Elm Language Support: Show type` command from the command palette
6 | - Shrinking the type panel to a height of 1 line will show just an enhanced type signature, better than the status bar, while expanding its height will show you documentation
7 | - Close the type panel with Escape or alt+down
8 | - If the elm-oracle features aren't working, elm-oracle needs to either be on your PATH, or you can add additional directories to the Elm Language Support > "elm_paths" setting
9 |
10 | Other than that:
11 | - elm_format_on_save now defaults to true. If you have elm-format installed, you probably want to be using it more often than not. You can still disable it by changing elm_format_on_save to false
12 | - Fixed a couple minor bugs, the most notable being Elm Make was broken on Windows with the most recent version of Elm
13 |
--------------------------------------------------------------------------------
/Build Systems/Elm Make.sublime-build:
--------------------------------------------------------------------------------
1 | {
2 | "target": "elm_make",
3 | "selector": "source.elm",
4 | "cmd":
5 | [
6 | "elm-make",
7 | "$file",
8 | "--output={null}",
9 | "--report=json",
10 | "--yes"
11 | ],
12 | "working_dir": "$project_path",
13 | "file_regex": "^\\-\\- \\w+: (?=.+ \\- (.+?):(\\d+):(\\d+))(.+) \\- .*$",
14 | "error_format": "-- $type: $tag - $file:$line:$column\n$message\n",
15 | "info_format": ":: $info\n",
16 | "syntax": "Packages/Elm Language Support/Syntaxes/Elm Compile Messages.hidden-tmLanguage",
17 | "color_scheme": "Packages/Color Scheme - Default/Sunburst.tmTheme",
18 | "null_device": "/dev/null",
19 | "warnings": "true",
20 | "osx":
21 | {
22 | "path": "/usr/local/bin:$PATH"
23 | },
24 | "linux":
25 | {
26 | "path": "$HOME/.cabal/bin:/usr/local/bin:$PATH"
27 | },
28 | "variants":
29 | [
30 | {
31 | "name": "Run",
32 | "cmd":
33 | [
34 | "elm-make",
35 | "$file",
36 | "--output={output}",
37 | "--report=json",
38 | "--yes"
39 | ]
40 | },
41 | {
42 | "name": "Ignore Warnings",
43 | "warnings": "false"
44 | },
45 | {
46 | "name": "Run - Ignore Warnings",
47 | "warnings": "false",
48 | "cmd":
49 | [
50 | "elm-make",
51 | "$file",
52 | "--output={output}",
53 | "--report=json",
54 | "--yes"
55 | ]
56 | }
57 | ]
58 | }
59 |
--------------------------------------------------------------------------------
/Commands/Project.sublime-commands:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "caption": "Elm Build System: View elm-package.json",
4 | "command": "elm_project"
5 | },
6 | {
7 | "caption": "Elm Build System: Set Main Path",
8 | "command": "elm_project", "args":
9 | {
10 | "prop_name": "main_path",
11 | "caption": "Enter main path to build from: "
12 | }
13 | },
14 | {
15 | "caption": "Elm Build System: Set HTML Path",
16 | "command": "elm_project", "args":
17 | {
18 | "prop_name": "html_path",
19 | "caption": "Enter HTML path to open in browser: "
20 | }
21 | },
22 | {
23 | "caption": "Elm Build System: Set Output Path",
24 | "command": "elm_project", "args":
25 | {
26 | "prop_name": "output_path",
27 | "caption": "Enter path to build output to: "
28 | }
29 | },
30 | {
31 | "caption": "Elm Build System: Set Output Directory",
32 | "command": "elm_project", "args":
33 | {
34 | "prop_name": "output_dir",
35 | "caption": "Enter directory to build output to: "
36 | }
37 | },
38 | {
39 | "caption": "Elm Build System: Set Output Base Name",
40 | "command": "elm_project", "args":
41 | {
42 | "prop_name": "output_name",
43 | "caption": "Enter base name to build output with: "
44 | }
45 | },
46 | {
47 | "caption": "Elm Build System: Set Output Extension",
48 | "command": "elm_project", "args":
49 | {
50 | "prop_name": "output_ext",
51 | "choices":
52 | [
53 | "HTML",
54 | "JS"
55 | ]
56 | }
57 | }
58 | ]
59 |
--------------------------------------------------------------------------------
/elm_format.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import subprocess
4 | import os, os.path
5 | import re
6 | import sublime, sublime_plugin
7 |
8 |
9 | class ElmFormatCommand(sublime_plugin.TextCommand):
10 | def run(self, edit):
11 |
12 | # Hide the console window on Windows
13 | shell = False
14 | path_separator = ':'
15 | if os.name == "nt":
16 | shell = True
17 | path_separator = ';'
18 |
19 | settings = sublime.load_settings('Elm Language Support.sublime-settings')
20 | binary = settings.get('elm_format_binary', 'elm-format')
21 | path = settings.get('elm_paths', '')
22 | if path:
23 | old_path = os.environ['PATH']
24 | os.environ['PATH'] = os.path.expandvars(path + path_separator + '$PATH')
25 |
26 | command = [binary, self.view.file_name(), '--yes']
27 | p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=shell)
28 |
29 | if path:
30 | os.environ['PATH'] = old_path
31 |
32 | output, errors = p.communicate()
33 |
34 | if settings.get('debug', False):
35 | string_settings = sublime.load_settings('Elm User Strings.sublime-settings')
36 | print(string_settings.get('logging.prefix', '') + '(' + binary + ') ' + str(output.strip()), '\nerrors: ' + str(errors.strip()))
37 | if str(errors.strip()):
38 | print('Your PATH is: ', os.environ['PATH'])
39 |
40 |
41 | class ElmFormatOnSave(sublime_plugin.EventListener):
42 | def on_post_save(self, view):
43 | sel = view.sel()[0]
44 | region = view.word(sel)
45 | scope = view.scope_name(region.b)
46 | if scope.find('source.elm') != -1:
47 | settings = sublime.load_settings('Elm Language Support.sublime-settings')
48 | if settings.get('elm_format_on_save', True):
49 | regex = settings.get('elm_format_filename_filter', '')
50 | if not (len(regex) > 0 and re.search(regex, view.file_name()) is not None):
51 | view.run_command('elm_format')
52 |
--------------------------------------------------------------------------------
/Menus/Main.sublime-menu:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "tools",
4 | "children":
5 | [{
6 | "id": "SublimeREPL",
7 | "caption": "SublimeREPL",
8 | "mnemonic": "R",
9 | "children":
10 | [
11 | {
12 | "caption": "Elm",
13 | "id": "repl_elm",
14 | "command": "repl_open", "args":
15 | {
16 | "type": "subprocess",
17 | "encoding": "utf8",
18 | "cmd": ["elm-repl"],
19 | "cwd": "$file_path",
20 | "syntax": "Packages/Elm Language Support/Syntaxes/Elm.tmLanguage",
21 | "external_id": "elm",
22 | "extend_env":
23 | {
24 | "osx": {"PATH": "{PATH}:/usr/local/bin"},
25 | "linux": {"PATH": "{PATH}:/usr/local/bin:{HOME}/.cabal/bin"},
26 | "windows": {}
27 | }
28 | }
29 | }
30 | ]
31 | }]
32 | },
33 | {
34 | "id": "preferences",
35 | "children":
36 | [{
37 | "id": "package-settings",
38 | "children":
39 | [
40 | {
41 | "caption": "Elm Language Support",
42 | "children":
43 | [
44 | {
45 | "caption": "Settings",
46 | "command": "edit_settings",
47 | "args": {
48 | "base_file": "${packages}/Elm Language Support/Settings/Elm Language Support.sublime-settings",
49 | "default": "// Settings in here override those in \"Elm Language Support/Settings/Elm Language Support.sublime-settings\",\n\n{\n\t$0\n}\n"
50 | }
51 | }
52 | ]
53 | }
54 | ]
55 | }]
56 | }
57 | ]
58 |
--------------------------------------------------------------------------------
/elm_plugin.py:
--------------------------------------------------------------------------------
1 | import sublime
2 | import sublime_plugin
3 | import os.path as fs
4 |
5 | def is_ST2():
6 | return sublime.version().startswith('2')
7 |
8 | def get_string(key, *args):
9 | strings = sublime.load_settings('Elm User Strings.sublime-settings')
10 | return strings.get('logging.prefix') + strings.get(key).format(*args)
11 |
12 | def log_string(key, *args):
13 | def log_string_with_retry(retry):
14 | try:
15 | # ST2: RuntimeError: Must call on main thread
16 | settings = sublime.load_settings('Elm Language Support.sublime-settings')
17 | except RuntimeError:
18 | if retry:
19 | sublime.set_timeout(lambda: log_string_with_retry(False), 0)
20 | else:
21 | import traceback
22 | traceback.print_exc()
23 | else:
24 | if settings.get('debug'):
25 | print(get_string(key, *args))
26 |
27 | log_string_with_retry(True)
28 |
29 | def import_module(path):
30 | names = path.split('.')
31 | index = 1 if is_ST2() else 0
32 | base = __import__(names[index])
33 | for name in names[index + 1:]:
34 | base = getattr(base, name)
35 | return base
36 |
37 | # defer import as long as possible in case plugin not loaded
38 | def replace_base_class(path):
39 | def splice_bases(old_base, *extra_bases):
40 | try:
41 | new_base = import_module(path)
42 | except ImportError:
43 | module_name = path[:path.index('.')]
44 | log_string('logging.missing_plugin', module_name)
45 | return None
46 | else:
47 | return (new_base,) + extra_bases
48 |
49 | def monkey_patch(target_cls):
50 | if not hasattr(target_cls, 'is_patched'):
51 | new_bases = splice_bases(target_cls.__bases__)
52 | target_cls.is_patched = bool(new_bases)
53 | if new_bases:
54 | target_cls.__bases__ = new_bases
55 |
56 | def decorator(target_cls):
57 | def new(cls, *args, **kwargs):
58 | monkey_patch(target_cls)
59 | super_ = super(target_cls, cls).__new__
60 | # TypeError: object() takes no parameters
61 | return super_(cls) if super_ is object.__new__ else super_(cls, *args, **kwargs)
62 |
63 | assert '__new__' not in target_cls.__dict__
64 | # ST2: TypeError: unbound method new()
65 | target_cls.__new__ = classmethod(new)
66 | return target_cls
67 |
68 | return decorator
69 |
--------------------------------------------------------------------------------
/Syntaxes/Elm Compile Messages.YAML-tmLanguage:
--------------------------------------------------------------------------------
1 | # [PackageDev] target_format: plist, ext: hidden-tmLanguage
2 | ---
3 | name: Elm Compile Messages
4 | scopeName: text.html.mediawiki.elm-build-output
5 | fileTypes: []
6 | uuid: 0e1a8891-7cc0-4991-8018-252d6f591f8f
7 |
8 | patterns:
9 | - comment: "|> Unparsed Compile Message"
10 | name: comment.line.heading.3.elm-build-output
11 | begin: '^(::) '
12 | end: '^\n$'
13 | patterns:
14 | - comment: elm-lang/core OR build\index.html
15 | name: markup.underline.link.elm-build-output
16 | match: \S+[/\.]\S+
17 | - comment: Successfully generated
18 | name: constant.language.boolean.true.elm-build-output
19 | match: (?i)\bsuccess\w+
20 | - comment: -- TAG - file:line:column\nOverview\nDetail\n
21 | name: meta.report.elm-build-output
22 | contentName: string.unquoted.elm-build-output
23 | begin: |-
24 | (?x) # Minimally modified `file_regex` from `Elm Make.sublime-build`
25 | ^\-\-[ ] # Leading delimiter
26 | ((error) # \2: error
27 | |(warning) # \3: warning
28 | |\w+ # \1: any $type
29 | )[:][ ] # separator
30 | (.+) # \4: tag
31 | [ ][-][ ] # separator
32 | (.+?): # \5: $file
33 | (\d+): # \6: $line
34 | (\d+) # \7: $column
35 | \n$ # End
36 | beginCaptures:
37 | '0': {name: markup.heading.4.elm-build-output}
38 | '1': {name: support.constant.type.elm-build-output}
39 | '2': {name: invalid.illegal.error.elm-build-output}
40 | '3': {name: invalid.deprecated.warning.elm-build-output}
41 | '4': {name: support.constant.type.elm-build-output}
42 | '5': {name: markup.underline.link.elm-build-output}
43 | '6': {name: constant.numeric.elm-build-output}
44 | '7': {name: constant.numeric.elm-build-output}
45 | end: ^\n$
46 | endCaptures:
47 | '0': {name: meta.separator.elm-build-output}
48 | patterns:
49 | - comment: 'Inline `variable`'
50 | name: markup.raw.inline.elm-build-output
51 | contentName: variable.other.elm.elm-build-output
52 | begin: (`)(?!`)
53 | end: \1
54 | captures:
55 | '0': {name: punctuation.definition.raw.elm-build-output}
56 | - comment: Code Block
57 | name: markup.raw.block.elm-build-output
58 | begin: (?m)^ {4}
59 | end: \n+(?!^ {4})
60 | patterns:
61 | - include: source.elm
62 | - comment: [Finished in 4.2s]
63 | name: comment.line.brackets.elm-build-output
64 | begin: ^\[
65 | end: \]$
66 | patterns:
67 | - comment: 4.2s
68 | name: constant.numeric.elm-build-output
69 | match: \b\d+\.\d+(s)\b
70 | captures:
71 | '1': {name: keyword.other.unit.elm-build-output}
72 | ...
73 |
--------------------------------------------------------------------------------
/elm_make.py:
--------------------------------------------------------------------------------
1 | import json
2 | import re
3 | import string
4 |
5 | try: # ST3
6 | from .elm_plugin import *
7 | from .elm_project import ElmProject
8 | except: # ST2
9 | from elm_plugin import *
10 | from elm_project import ElmProject
11 | default_exec = import_module('Default.exec')
12 |
13 | @replace_base_class('Highlight Build Errors.HighlightBuildErrors.ExecCommand')
14 | class ElmMakeCommand(default_exec.ExecCommand):
15 |
16 | # inspired by: http://www.sublimetext.com/forum/viewtopic.php?t=12028
17 | def run(self, error_format, info_format, syntax, color_scheme, null_device, warnings, **kwargs):
18 | self.buffer = b''
19 | self.warnings = warnings == "true"
20 | self.error_format = string.Template(error_format)
21 | self.info_format = string.Template(info_format)
22 | self.run_with_project(null_device=null_device, **kwargs)
23 | self.style_output(syntax, color_scheme)
24 |
25 | def run_with_project(self, cmd, working_dir, null_device, **kwargs):
26 | file_arg, output_arg = cmd[1:3]
27 | project = ElmProject(file_arg)
28 | log_string('project.logging.settings', repr(project))
29 | if '{output}' in output_arg:
30 | cmd[1] = fs.expanduser(project.main_path)
31 | output_path = fs.expanduser(project.output_path)
32 | cmd[2] = output_arg.format(output=output_path)
33 | else:
34 | # cmd[1] builds active file rather than project main
35 | cmd[2] = output_arg.format(null=null_device)
36 | project_dir = project.working_dir or working_dir
37 | # ST2: TypeError: __init__() got an unexpected keyword argument 'syntax'
38 | super(ElmMakeCommand, self).run(cmd, working_dir=project_dir, **kwargs)
39 |
40 | def style_output(self, syntax, color_scheme):
41 | self.output_view.set_syntax_file(syntax)
42 | self.output_view.settings().set('color_scheme', color_scheme)
43 | if self.is_patched:
44 | self.debug_text = ''
45 | else:
46 | self.debug_text = get_string('make.missing_plugin')
47 |
48 | def on_data(self, proc, data):
49 | self.buffer += data
50 |
51 | def on_finished(self, proc):
52 | result_strs = self.buffer.decode(self.encoding).split('\n')
53 | flat_map = lambda f ,xss: sum(map(f, xss), [])
54 | output_strs = flat_map(self.format_result, result_strs) + ['']
55 | output_data = '\n'.join(output_strs).encode(self.encoding)
56 | super(ElmMakeCommand, self).on_data(proc, output_data)
57 | super(ElmMakeCommand, self).on_finished(proc)
58 |
59 | def format_result(self, result_str):
60 | decode_error = lambda dict: self.format_error(**dict) if 'type' in dict else dict
61 | try:
62 | data = json.loads(result_str, object_hook=decode_error)
63 | return [s for s in data if s is not None]
64 | except ValueError:
65 | log_string('make.logging.invalid_json', result_str)
66 | info_str = result_str.strip()
67 | return [self.info_format.substitute(info=info_str)] if info_str else []
68 |
69 | def format_error(shelf, type, file, region, tag, overview, details, **kwargs):
70 | if type == 'warning' and not shelf.warnings:
71 | return None
72 | line = region['start']['line']
73 | column = region['start']['column']
74 | message = overview
75 | if details:
76 | message += '\n' + re.sub(r'(\n)+', r'\1', details)
77 | # TypeError: substitute() got multiple values for argument 'self'
78 | # https://bugs.python.org/issue23671
79 | return shelf.error_format.substitute(**locals())
80 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 | # The Sublime Elm Language Package
3 |
4 | ## Installation
5 |
6 | 1. Install [Package Control][]
7 | 2. Run `Package Control: Install Package` in the Command Palette (Super+Shift+P)
8 | 3. Install [Elm][] or use [NPM][] (`npm i -g elm`)
9 |
10 | ## Features
11 |
12 | - Compatible with [Sublime Text 2] and [Sublime Text 3]
13 | - Syntax highlighting
14 | - Snippets for common Elm syntax (function, `case` … `of`, `let` … `in`, etc.)
15 | - Autocompletions plus type signature and documentation display for all functions inside packages in your `elm-package.json` file (requires [elm-oracle](https://www.npmjs.com/package/elm-oracle), which you can install with `npm install -g elm-oracle`)
16 | 1. Bring up the type panel with `alt+up` or through the right-click context menu
17 | 2. Close the type panel with `alt+down`
18 | 3. If you don't like these keybindings, rebind them in your User packages directory
19 | 
20 | - Four standard build commands (Super+[Shift]+B or Super+[Shift]+F7)
21 | 1. `Build` just checks errors. Kudos to this [tweet][]!
22 | 2. `Run` additionally outputs your compiled program to an inferred path.
23 | 3. The same as the above two, but ignoring warnings
24 | 4. Output path is configurable in `elm-package.json` or `Elm Build System: …` in the Command Palette. Elm build system only requires a valid config in any ancestor directory of the active file. 
25 | - Compile messages
26 | 1. Navigate errors and warnings (Super+[Shift]+F4).
27 | 2. Formatted for build output panel.
28 | 3. Compile message highlighting, embedded code highlighting, and color scheme for output panel. 
29 | - Integration with popular plugins (installed separately)
30 | 1. [SublimeREPL][] — Run `elm-repl` in an editor tab with syntax highlighting. 
31 | 2. [Highlight Build Errors][] — Does what it says on the box … usually.
32 | - Integration with [elm format](https://github.com/avh4/elm-format)
33 | 1. Make sure `elm-format` is in your PATH
34 | 2. Run the "Elm Language Support: Run elm-format" command from the Command Palette to run elm-format on the current file
35 | 3. To enable automatic formatting on every save, Go to Preferences -> Package Settings -> Elm Language Support -> User and add this setting:
36 | `"elm_format_on_save": true`
37 | 4. If there are certain Elm source files you don't want to automatically run `elm-format` on, for example elm-css based files, you can set a regex filter which will search the full filename (including the path to the file). If the regex matches, then it will not automatically run `elm-format` on the file when you save. For example, the following filter would prevent automatic `elm-format` on a file named `elm-css/src/Css/TopBar.elm`:
38 | `"elm_format_filename_filter": "elm-css/src/Css/.*\\.elm$"`
39 |
40 | ## Troubleshooting
41 |
42 | - I have `elm-oracle` installed, but completions, type signature display, and the type panel don't work
43 | 1. Make sure `elm-oracle` is on your PATH, or
44 | 2. Add the absolute path of the directory containing `elm-oracle` to the `elm_paths` setting in your Elm Language Support User settings
45 | - I have `elm-format` installed, but it's not working
46 | 1. Make sure `elm-format` is on your PATH, or
47 | 2. If using an alternate name for the binary (`elm-format-0.17` or `elm-format-0.18`) add it to the `elm_format_binary` setting in your Elm Language Support User settings; an example might be `"elm_format_binary": "elm-format-0.18",`, or
48 | 3. Add the absolute path of the directory containing `elm-format` to the `elm_paths` setting in your Elm Language Support User settings. Note that you can combine paths with the above, so an example might be `"elm_paths": "/users/alex/elm-format:/users/alex/elm-oracle"`
49 | - Elm format automatically runs every time I save a file, but there are some files I don't want it to run on
50 | 1. If there are certain Elm source files you don't want to automatically run `elm-format` on, for example elm-css based files, you can set a regex filter which will search the full filename (including the path to the file). If the regex matches, then it will not automatically run `elm-format` on the file when you save. For example, the following filter would prevent automatic `elm-format` on a file named `elm-css/src/Css/TopBar.elm`:
51 | `"elm_format_filename_filter": "elm-css/src/Css/.*\\.elm$"`
52 |
53 | ## Learning
54 |
55 | Don't know Elm? Great first step!
56 |
57 | - [Elm Website][]
58 | - [elm-discuss group][]
59 | - [Pragmatic Studio: Building Web Apps with Elm][pragmatic]
60 | - [Elm Town Podcast][]
61 |
62 | [elm-discuss group]: https://groups.google.com/d/forum/elm-discuss
63 | [Elm]: http://elm-lang.org/install
64 | [Elm Town Podcast]: https://elmtown.github.io
65 | [Elm Website]: http://elm-lang.org
66 | [Highlight Build Errors]: https://packagecontrol.io/packages/Highlight%20Build%20Errors
67 | [NPM]: https://nodejs.org
68 | [Package Control]: https://packagecontrol.io/installation
69 | [pragmatic]: https://pragmaticstudio.com/elm
70 | [SublimeREPL]: https://packagecontrol.io/packages/SublimeREPL
71 | [Sublime Text 2]: http://www.sublimetext.com/2
72 | [Sublime Text 3]: http://www.sublimetext.com/3
73 | [tweet]: https://twitter.com/rtfeldman/status/624026168652660740
74 |
--------------------------------------------------------------------------------
/Syntaxes/Elm Compile Messages.hidden-tmLanguage:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileTypes
6 |
7 | name
8 | Elm Compile Messages
9 | patterns
10 |
11 |
12 | begin
13 | ^(::)
14 | comment
15 | |> Unparsed Compile Message
16 | end
17 | ^\n$
18 | name
19 | comment.line.heading.3.elm-build-output
20 | patterns
21 |
22 |
23 | comment
24 | elm-lang/core OR build\index.html
25 | match
26 | \S+[/\.]\S+
27 | name
28 | markup.underline.link.elm-build-output
29 |
30 |
31 | comment
32 | Successfully generated
33 | match
34 | (?i)\bsuccess\w+
35 | name
36 | constant.language.boolean.true.elm-build-output
37 |
38 |
39 |
40 |
41 | begin
42 | (?x) # Minimally modified `file_regex` from `Elm Make.sublime-build`
43 | ^\-\-[ ] # Leading delimiter
44 | ((error) # \2: error
45 | |(warning) # \3: warning
46 | |\w+ # \1: any $type
47 | )[:][ ] # separator
48 | (.+) # \4: tag
49 | [ ][-][ ] # separator
50 | (.+?): # \5: $file
51 | (\d+): # \6: $line
52 | (\d+) # \7: $column
53 | \n$ # End
54 | beginCaptures
55 |
56 | 0
57 |
58 | name
59 | markup.heading.4.elm-build-output
60 |
61 | 1
62 |
63 | name
64 | support.constant.type.elm-build-output
65 |
66 | 2
67 |
68 | name
69 | invalid.illegal.error.elm-build-output
70 |
71 | 3
72 |
73 | name
74 | invalid.deprecated.warning.elm-build-output
75 |
76 | 4
77 |
78 | name
79 | support.constant.type.elm-build-output
80 |
81 | 5
82 |
83 | name
84 | markup.underline.link.elm-build-output
85 |
86 | 6
87 |
88 | name
89 | constant.numeric.elm-build-output
90 |
91 | 7
92 |
93 | name
94 | constant.numeric.elm-build-output
95 |
96 |
97 | comment
98 | -- TAG - file:line:column\nOverview\nDetail\n
99 | contentName
100 | string.unquoted.elm-build-output
101 | end
102 | ^\n$
103 | endCaptures
104 |
105 | 0
106 |
107 | name
108 | meta.separator.elm-build-output
109 |
110 |
111 | name
112 | meta.report.elm-build-output
113 | patterns
114 |
115 |
116 | begin
117 | (`)(?!`)
118 | captures
119 |
120 | 0
121 |
122 | name
123 | punctuation.definition.raw.elm-build-output
124 |
125 |
126 | comment
127 | Inline `variable`
128 | contentName
129 | variable.other.elm.elm-build-output
130 | end
131 | \1
132 | name
133 | markup.raw.inline.elm-build-output
134 |
135 |
136 | begin
137 | (?m)^ {4}
138 | comment
139 | Code Block
140 | end
141 | \n+(?!^ {4})
142 | name
143 | markup.raw.block.elm-build-output
144 | patterns
145 |
146 |
147 | include
148 | source.elm
149 |
150 |
151 |
152 |
153 |
154 |
155 | begin
156 | ^\[
157 | comment
158 |
159 | Finished in 4.2s
160 |
161 | end
162 | \]$
163 | name
164 | comment.line.brackets.elm-build-output
165 | patterns
166 |
167 |
168 | captures
169 |
170 | 1
171 |
172 | name
173 | keyword.other.unit.elm-build-output
174 |
175 |
176 | comment
177 | 4.2s
178 | match
179 | \b\d+\.\d+(s)\b
180 | name
181 | constant.numeric.elm-build-output
182 |
183 |
184 |
185 |
186 | scopeName
187 | text.html.mediawiki.elm-build-output
188 | uuid
189 | 0e1a8891-7cc0-4991-8018-252d6f591f8f
190 |
191 |
192 |
--------------------------------------------------------------------------------
/elm_generate.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import sys
4 |
5 | class Module(object):
6 | def __init__(self, data):
7 | self.name = data['name']
8 | self.values = [name(v['raw']) + ' : ' + signature(v['raw']) for v in data['values']]
9 | self.valueNames = [name(v) for v in self.values]
10 | self.datatypes = [v['name'] for v in data['datatypes']]
11 | self.constructors = [[v['name'] for v in x['constructors']] for x in data['datatypes']]
12 | self.aliases = [v['name'] for v in data['aliases']]
13 |
14 | def include_text(self):
15 | s = '\n\tinclude\n\t#{}\n'.format(self.name.lower())
16 | return s
17 |
18 | def moduleText(self):
19 | s = '{nameLower}\n\n\tcaptures\n\t\n\t\t1\n\t\t\n\t\t\tname\n\t\t\tvariable.parameter\n\t\t\n\t\t2\n\t\t\n\t\t\tname\n\t\t\tvariable.parameter\n\t\t\n\t\t3\n\t\t\n\t\t\tname\n\t\t\tsupport.function.elm\n\t\t\n\t\n\tmatch\n\t\\b({name})(.)({values})\\b\n\tname\n\tvariable.parameter\n'
20 | values = '|'.join([n for n in self.valueNames if not n.startswith('(')])
21 | if self.aliases:
22 | values += '|' + '|'.join(self.aliases)
23 | if self.datatypes:
24 | values += '|' + '|'.join(self.datatypes)
25 | return s.format(nameLower=self.name.lower(), name=self.name, values=values)
26 |
27 | def snippets(self):
28 | base = 'Snippets'
29 | s = '\n\t\n\t\n\t{name}\n\t\n\tsource.elm\n\t{signature}\n'
30 | for v in [func for func in self.values if not name(func).startswith('(')]:
31 | subdirectories = self.name.split('.')
32 | path = '{}' + '\\{}'*(len(subdirectories))
33 | path = path.format(base, *subdirectories)
34 |
35 | if not os.path.exists(path):
36 | os.makedirs(path)
37 |
38 | path += '\\{}'
39 |
40 | with open(path.format(name(v) + '.sublime-snippet'), 'w') as f:
41 | f.write(s.format(autocomplete=make_autocomplete(v), name=name(v), signature=signature(v)))
42 |
43 | print('Wrote {}'.format(path.format(name(v) + '.sublime-snippet')))
44 |
45 | def name(t):
46 | return t.split(' : ')[0].strip()
47 |
48 | def signature(t):
49 | return t.split(' : ')[1].strip()
50 |
51 | def hintize(t):
52 | first = t[0].lower()
53 | t = t.replace(' ', '')
54 | return first + ''.join(t[1:])
55 |
56 | def typeFormat(t):
57 | if t[0] == '[':
58 | return 'ListOf' + typeFormat(t[1:-1])
59 | elif t[0] == '(':
60 | return ''.join([unicode(v.strip()) for v in t[1:-1].split(',')]) + 'Tuple'
61 | else:
62 | if len(t.split(' ')) == 1:
63 | return t
64 | else:
65 | x = t.split(' ')
66 | return x[0] + ''.join([typeFormat(v) for v in x[1:]])
67 |
68 | def tokenize(t):
69 | return [v.strip() for v in t.split('->')]
70 |
71 | def print_type(t):
72 | print(name(t))
73 | print([typeFormat(v) for v in tokenize(signature(t))])
74 |
75 | def make_autocomplete(t):
76 | s = '{}'.format(name(t))
77 | args = arguments(signature(t))
78 | for n, arg in enumerate(args):
79 | s += ' ${{{n}:{arg}}}'.format(n=n+1, arg=arg)
80 | return s
81 |
82 | def arguments(signature):
83 | args = [v.strip() for v in signature.split('->')][:-1]
84 | new_args = []
85 | open_parens = 0
86 | for arg in args:
87 | parens = arg.count('(') - arg.count(')')
88 | if parens and not open_parens:
89 | new_args.append('function')
90 | elif open_parens != 0:
91 | open_parens += parens
92 | continue
93 | else:
94 | new_args.append(argify(arg))
95 | open_parens += parens
96 | return new_args
97 |
98 | def argify(s):
99 | if s.startswith('('):
100 | return 'tuple'
101 | elif s.startswith('['):
102 | return 'list'
103 | elif len(s.split(' ')) > 1:
104 | return s.split(' ')[0].lower()
105 | else:
106 | return s.lower()
107 |
108 | def loadDocs(path):
109 | with open(path) as f:
110 | return json.load(f)
111 |
112 |
113 | if __name__ == '__main__':
114 | ## Usage: pass in docs.json from cabal's elm directory
115 | path = sys.argv[1]
116 | prelude = ['Basics', 'List', 'Signal', 'Text', 'Maybe', 'Time', 'Graphics.Element', 'Color', 'Graphics.Collage']
117 |
118 | modules = [Module(m) for m in loadDocs(path)]
119 |
120 | print('Prelude:')
121 | print('show|')
122 | for m in modules:
123 | if m.name in prelude:
124 | print('|'.join([n for n in m.valueNames if not n.startswith('(')]))
125 |
126 | print('\n'*5)
127 |
128 | print('Prelude Aliases and Datatypes:')
129 | print('Int|Float|Char|Bool|String|True|False')
130 | for m in modules:
131 | if m.name in prelude:
132 | print('|'.join([n for n in (m.datatypes + m.aliases) if not n.startswith('(')]) + '|')
133 |
134 | print('\n'*5)
135 |
136 | print('Includes:')
137 | for m in modules:
138 | print(m.include_text())
139 |
140 | print('\n'*5)
141 |
142 | print('Includes Continued:')
143 | for m in modules:
144 | print(m.moduleText())
145 |
146 | print('\n'*5)
147 |
148 | print('Constructors:')
149 | print('\(\)|\[\]|True|False|Int|Char|Bool|String|')
150 | for m in modules:
151 | if m.name in prelude:
152 | for c in m.constructors:
153 | print('|'.join(c) + '|')
154 |
155 | print('\n'*5)
156 |
157 | print('Writing Autocompletion Snippets...:')
158 | for m in modules:
159 | if m.name in prelude:
160 | m.snippets()
161 | print('\n'*2)
162 |
163 | with open('Snippets\\Basics\\markdown.sublime-snippet', 'w') as f:
164 | f.write('\n\n\nmarkdown\n\nsource.elm\nA markdown block\n')
165 | print('Wrote markdown.sublime-snippet')
166 |
--------------------------------------------------------------------------------
/elm_project.py:
--------------------------------------------------------------------------------
1 | import collections
2 | import json
3 |
4 | try: # ST3
5 | from .elm_plugin import *
6 | except: # ST2
7 | from elm_plugin import *
8 |
9 | class ElmProjectCommand(sublime_plugin.TextCommand):
10 |
11 | def is_enabled(self):
12 | self.project = ElmProject(self.view.file_name())
13 | return self.project.exists
14 |
15 | def run(self, edit, prop_name=None, choices=None, caption=None):
16 | self.window = self.view.window()
17 | if not prop_name:
18 | self.window.open_file(self.project.json_path, sublime.TRANSIENT)
19 | return
20 | self.prop_name = prop_name
21 | initial_value = getattr(self.project, prop_name)
22 | if choices:
23 | self.show_choices(choices, initial_value)
24 | else:
25 | self.window.show_input_panel(caption, initial_value, self.on_finished, None, None)
26 |
27 | def show_choices(self, choices, initial_value):
28 | self.norm_choices = [choice.lower() for choice in choices]
29 | try:
30 | # ValueError: $initial_value is not in list
31 | initial_index = self.norm_choices.index(initial_value.lower())
32 | # ST2: Boost.Python.ArgumentError: Python argument types
33 | self.window.show_quick_panel(choices, self.on_choice, selected_index=initial_index)
34 | except: # simplest control flow
35 | if not is_ST2():
36 | log_string('project.logging.invalid_choice', initial_value)
37 | self.window.show_quick_panel(choices, self.on_choice)
38 |
39 | def on_choice(self, index):
40 | if index != -1:
41 | self.on_finished(self.norm_choices[index])
42 |
43 | def on_finished(self, value):
44 | setattr(self.project, self.prop_name, value)
45 | keys = self.project._last_updated_key_path
46 | if keys:
47 | sublime.status_message(get_string('project.updated', '.'.join(keys), value))
48 |
49 | BUILD_KEY = ('sublime-build',)
50 | MAIN_KEY = BUILD_KEY + ('main',)
51 | HTML_KEY = BUILD_KEY + ('html',)
52 | OUTPUT_KEY = BUILD_KEY + ('output',)
53 | OUTPUT_PATH_KEY = OUTPUT_KEY + ('path',)
54 | OUTPUT_COMP_KEY = OUTPUT_KEY + ('components',)
55 | OUTPUT_DIR_KEY = OUTPUT_COMP_KEY + ('dir',)
56 | OUTPUT_NAME_KEY = OUTPUT_COMP_KEY + ('name',)
57 | OUTPUT_EXT_KEY = OUTPUT_COMP_KEY + ('ext',)
58 |
59 | class ElmProject(object):
60 |
61 | @classmethod
62 | def find_json(cls, dir_path):
63 | if not fs.isdir(fs.abspath(dir_path)):
64 | return None
65 | file_path = fs.abspath(fs.join(dir_path, 'elm-package.json'))
66 | if fs.isfile(file_path):
67 | return file_path
68 | parent_path = fs.join(dir_path, fs.pardir)
69 | if fs.abspath(parent_path) == fs.abspath(dir_path):
70 | return None
71 | return cls.find_json(parent_path)
72 |
73 | def __init__(self, file_path):
74 | self.file_path = file_path
75 | self.json_path = self.find_json(fs.dirname(file_path or ''))
76 | self.data_dict = self.load_json()
77 |
78 | def __getitem__(self, keys):
79 | if not self.exists:
80 | return None
81 | item = self.data_dict
82 | for key in keys:
83 | item = item.get(key)
84 | if not item:
85 | break
86 | return item
87 |
88 | def __setitem__(self, keys, value):
89 | self._last_updated_key_path = None
90 | if not self.exists:
91 | sublime.error_message(get_string('project.not_found'))
92 | return
93 | item = self.data_dict
94 | for key in keys[0:-1]:
95 | item = item.setdefault(key, {})
96 | item[keys[-1]] = value
97 | self.save_json()
98 | self._last_updated_key_path = keys
99 |
100 | def __repr__(self):
101 | members = [(name, getattr(self, name), ' ' * 4)
102 | for name in dir(self) if name[0] != '_']
103 | properties = ["{indent}{name}={value},".format(**locals())
104 | for name, value, indent in members if not callable(value)]
105 | return "{0}(\n{1}\n)".format(self.__class__.__name__, '\n'.join(properties))
106 |
107 | def load_json(self):
108 | try:
109 | with open(self.json_path) as json_file:
110 | if is_ST2(): # AttributeError: 'module' object has no attribute 'OrderedDict'
111 | return json.load(json_file)
112 | else:
113 | return json.load(json_file, object_pairs_hook=collections.OrderedDict)
114 | except TypeError: # self.json_path == None
115 | pass
116 | except ValueError:
117 | log_string('project.logging.invalid_json', self.json_path)
118 | return None
119 |
120 | def save_json(self):
121 | with open(self.json_path, 'w') as json_file:
122 | json.dump(self.data_dict, json_file,
123 | indent=4,
124 | separators=(',', ': '),
125 | sort_keys=is_ST2())
126 |
127 | @property
128 | def exists(self):
129 | return bool(self.data_dict)
130 |
131 | @property
132 | def working_dir(self):
133 | return fs.dirname(self.json_path) if self.json_path else None
134 |
135 | @property
136 | def main_path(self):
137 | return self[MAIN_KEY] or fs.relpath(self.file_path, self.working_dir)
138 |
139 | @main_path.setter
140 | def main_path(self, value):
141 | self[MAIN_KEY] = value
142 |
143 | @property
144 | def html_path(self):
145 | return self[HTML_KEY] or self.output_path
146 |
147 | @html_path.setter
148 | def html_path(self, value):
149 | self[HTML_KEY] = value
150 |
151 | @property
152 | def output_path(self):
153 | output_path = fs.join(self.output_dir, self.output_name + '.' + self.output_ext)
154 | return self[OUTPUT_PATH_KEY] or fs.normpath(output_path)
155 |
156 | @output_path.setter
157 | def output_path(self, value):
158 | self[OUTPUT_PATH_KEY] = value
159 |
160 | @property
161 | def output_dir(self):
162 | return self[OUTPUT_DIR_KEY] or 'build'
163 |
164 | @output_dir.setter
165 | def output_dir(self, value):
166 | self[OUTPUT_DIR_KEY] = value
167 |
168 | @property
169 | def output_name(self):
170 | return self[OUTPUT_NAME_KEY] or fs.splitext(fs.basename(self.main_path))[0]
171 |
172 | @output_name.setter
173 | def output_name(self, value):
174 | self[OUTPUT_NAME_KEY] = value
175 |
176 | @property
177 | def output_ext(self):
178 | return self[OUTPUT_EXT_KEY] or 'html'
179 |
180 | @output_ext.setter
181 | def output_ext(self, value):
182 | self[OUTPUT_EXT_KEY] = value
183 |
--------------------------------------------------------------------------------
/Syntaxes/Elm.YAML-tmLanguage:
--------------------------------------------------------------------------------
1 | # [PackageDev] target_format: plist, ext: tmLanguage
2 | name: Elm
3 | scopeName: source.elm
4 | fileTypes: [elm]
5 | uuid: 2cb90e5e-6e98-456d-9a8a-b59935dbc4b0
6 |
7 | patterns:
8 | - name: keyword.operator.function.infix.elm
9 | match: (`)[a-zA-Z_']*?(`)
10 | captures:
11 | '1': {name: punctuation.definition.entity.elm}
12 | '2': {name: punctuation.definition.entity.elm}
13 |
14 | - name: constant.language.unit.elm
15 | match: \(\)
16 |
17 | - name: meta.declaration.module.elm
18 | begin: ^\b((effect|port)\s+)?(module)\s+
19 | beginCaptures:
20 | '1': {name: keyword.other.elm}
21 | '3': {name: keyword.other.elm}
22 | end: $|;
23 | endCaptures:
24 | '1': {name: keyword.other.elm}
25 | patterns:
26 | - include: '#module_name'
27 | - begin: (where)\s*\{
28 | beginCaptures:
29 | '1': {name: keyword.other.elm}
30 | end: \}
31 | patterns:
32 | - include: '#type_signature'
33 | - name: keyword.other.elm
34 | match: (exposing)
35 | - include: '#module_exports'
36 | - name: keyword.other.elm
37 | match: (where)
38 | - name: invalid
39 | match: '[a-z]+'
40 |
41 | - name: meta.import.elm
42 | begin: ^\b(import)\s+((open)\s+)?
43 | beginCaptures:
44 | '1': {name: keyword.other.elm}
45 | '3': {name: invalid}
46 | end: ($|;)
47 | patterns:
48 | - name: keyword.import.elm
49 | match: (as|exposing)
50 | - include: '#module_name'
51 | - include: '#module_exports'
52 |
53 | - name: entity.glsl.elm
54 | begin: (\[)(glsl)(\|)
55 | beginCaptures:
56 | '1': {name: keyword.other.elm}
57 | '2': {name: support.function.prelude.elm}
58 | '3': {name: keyword.other.elm}
59 | end: (\|\])
60 | endCaptures:
61 | '1': {name: keyword.other.elm}
62 | patterns:
63 | - include: source.glsl
64 |
65 | - name: keyword.other.elm
66 | match: \b(type alias|type|case|of|let|in|as)\s+
67 |
68 | - name: keyword.control.elm
69 | match: \b(if|then|else)\s+
70 |
71 | - comment: Floats are always decimal
72 | name: constant.numeric.float.elm
73 | match: \b([0-9]+\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)\b
74 |
75 | - name: constant.numeric.elm
76 | match: \b([0-9]+)\b
77 |
78 | - name: string.quoted.double.elm
79 | begin: '"""'
80 | beginCaptures:
81 | '0': {name: punctuation.definition.string.begin.elm}
82 | end: '"""'
83 | endCaptures:
84 | '0': {name: punctuation.definition.string.end.elm}
85 | patterns:
86 | - name: constant.character.escape.elm
87 | match: \\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\'\&])
88 | - name: constant.character.escape.control.elm
89 | match: \^[A-Z@\[\]\\\^_]
90 |
91 | - name: string.quoted.double.elm
92 | begin: '"'
93 | beginCaptures:
94 | '0': {name: punctuation.definition.string.begin.elm}
95 | end: '"'
96 | endCaptures:
97 | '0': {name: punctuation.definition.string.end.elm}
98 | patterns:
99 | - name: constant.character.escape.elm
100 | match: \\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\\"'\&])
101 | - name: constant.character.escape.control.elm
102 | match: \^[A-Z@\[\]\\\^_]
103 |
104 | - name: string.quoted.single.elm
105 | match: "(?x)\n(')\n(?:\n\t[\\ -\\[\\]-~]\t\t\t\t\t\t\t\t# Basic Char\n | (\\\\\
106 | (?:NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE\n\t\t|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS\n\
107 | \t\t|US|SP|DEL|[abfnrtv\\\\\\\"'\\&]))\t\t# Escapes\n | (\\^[A-Z@\\[\\]\\\\\\\
108 | ^_])\t\t\t\t\t\t# Control Chars\n)\n(')"
109 | captures:
110 | '1': {name: punctuation.definition.string.begin.elm}
111 | '2': {name: constant.character.escape.elm}
112 | '3': {name: punctuation.definition.string.end.elm}
113 |
114 | - name: meta.function.type-declaration.elm
115 | begin: ^(port\s+)?([a-z_][a-zA-Z0-9_']*|\([|!%$+\-.,=>]+\))\s*((:)([:]+)?)
116 | beginCaptures:
117 | '1': {name: keyword.other.port.elm}
118 | '2': {name: entity.name.function.elm}
119 | '4': {name: keyword.other.colon.elm}
120 | '5': {name: invalid}
121 | end: $\n?
122 | patterns:
123 | - include: '#type_signature'
124 |
125 | - name: keyword.other.port.elm
126 | match: \bport\s+
127 |
128 | - name: constant.other.elm
129 | match: \b[A-Z]\w*\b
130 |
131 | - include: '#comments'
132 |
133 | - name: entity.name.function.elm
134 | match: ^[a-z][A-Za-z0-9_']*\s+
135 |
136 | - include: '#infix_op'
137 |
138 | - name: keyword.operator.elm
139 | match: '[|!%$?~+:\-.=>&\\*^]+'
140 |
141 | # Note: Sublime color schemes consistently decline to apply colors to
142 | # delimiters of literals. This does Elm users a disservice since
143 | # Elm's function application syntax thrives on a clear visual distinction
144 | # between literal delimiters and expressions.
145 | # We therefore go out of our way to make sure delimiters are colorized.
146 | - name: constant.language.delimiter.elm
147 | match: '([\[\]\{\},])'
148 | captures:
149 | '1': {name: support.function.delimiter.elm}
150 |
151 | - name: keyword.other.parenthesis.elm
152 | match: '([\(\)])'
153 |
154 | repository:
155 | block_comment:
156 | name: comment.block.elm
157 | begin: \{-(?!#)
158 | end: -\}
159 | captures:
160 | '0': {name: punctuation.definition.comment.elm}
161 | patterns:
162 | - include: '#block_comment'
163 | applyEndPatternLast: 1
164 |
165 | comments:
166 | patterns:
167 | - name: comment.line.double-dash.elm
168 | match: (--).*$\n?
169 | captures:
170 | '1': {name: punctuation.definition.comment.elm}
171 | - include: '#block_comment'
172 |
173 | infix_op:
174 | name: entity.name.function.infix.elm
175 | match: (\([|!%$+:\-.=>]+\)|\(,+\))
176 |
177 | module_exports:
178 | name: meta.declaration.exports.elm
179 | begin: \(
180 | end: \)
181 | patterns:
182 | - name: entity.name.function.elm
183 | match: \b[a-z][a-zA-Z_'0-9]*
184 | - name: storage.type.elm
185 | match: \b[A-Z][A-Za-z_'0-9]*
186 | - name: punctuation.separator.comma.elm
187 | match: ','
188 | - include: '#infix_op'
189 | - comment: So named because I don't know what to call this.
190 | name: meta.other.unknown.elm
191 | match: \(.*?\)
192 |
193 | module_name:
194 | name: support.other.module.elm
195 | match: '[A-Z][A-Za-z._'']*'
196 |
197 | type_signature:
198 | patterns:
199 | - name: meta.class-constraint.elm
200 | match: \(\s*([A-Z][A-Za-z]*)\s+([a-z][A-Za-z_']*)\)\s*(=>)
201 | captures:
202 | '1': {name: entity.other.inherited-class.elm}
203 | '2': {name: variable.other.generic-type.elm}
204 | '3': {name: keyword.other.big-arrow.elm}
205 | - name: keyword.other.arrow.elm
206 | match: ->
207 | - name: keyword.other.big-arrow.elm
208 | match: =>
209 | - name: variable.other.generic-type.elm
210 | match: \b[a-z][a-zA-Z0-9_']*\b
211 | - name: storage.type.elm
212 | match: \b[A-Z][a-zA-Z0-9_']*\b
213 | - name: support.constant.unit.elm
214 | match: \(\)
215 | - include: '#comments'
216 |
--------------------------------------------------------------------------------
/elm_show_type.py:
--------------------------------------------------------------------------------
1 | from __future__ import print_function
2 |
3 | import webbrowser
4 | import os, os.path
5 | import subprocess
6 | import json
7 | import re
8 | from difflib import SequenceMatcher
9 |
10 | import sublime, sublime_plugin
11 |
12 | try: # ST3
13 | from .elm_project import ElmProject
14 | except: # ST2
15 | from elm_project import ElmProject
16 |
17 | LOOKUPS = {}
18 |
19 | def join_qualified(region, view):
20 | """
21 | Given a region, expand outward on periods to return a new region defining
22 | the entire word, in the context of Elm syntax.
23 |
24 | For example, when the region encompasses the 'map' part of a larger
25 | 'Dict.map' word, this function will return the entire region encompassing
26 | 'Dict.map'. The same is true if the region is encompassing 'Dict'.
27 |
28 | Recursively expands outward in both directions, correctly returning longer
29 | constructions such as 'Graphics.Input.button'
30 | """
31 | starting_region = region
32 | prefix = view.substr(region.a - 1)
33 | suffix = view.substr(region.b)
34 | if prefix == '.':
35 | region = region.cover(view.word(region.a - 2))
36 | if suffix == '.':
37 | region = region.cover(view.word(region.b + 1))
38 |
39 | if region == starting_region:
40 | return region
41 | else:
42 | return join_qualified(region, view)
43 |
44 | def get_word_under_cursor(view):
45 | sel = view.sel()[0]
46 | region = join_qualified(view.word(sel), view)
47 | return view.substr(region).strip()
48 |
49 | def get_type(view, panel):
50 | """
51 | Given a view, return the type signature of the word under the cursor,
52 | if found. If no type is found, return an empty string. Write the info
53 | to an output panel.
54 | """
55 | sel = view.sel()[0]
56 | region = join_qualified(view.word(sel), view)
57 | scope = view.scope_name(region.b)
58 | if scope.find('source.elm') != -1 and scope.find('string') == -1 and scope.find('comment') == -1:
59 | filename = view.file_name()
60 | word = view.substr(region).strip()
61 | sublime.set_timeout_async(lambda: search_and_set_status_message(filename, word, panel, 0), 0)
62 |
63 | def search_and_set_status_message(filename, query, panel, tries):
64 | """
65 | Given a filename and a query, look up in the in-memory dict of values
66 | pulled from elm oracle to find a match. If a match is found, display
67 | the type signature in the status bar and set it in the output panel.
68 | """
69 | global LOOKUPS
70 | if len(query) == 0:
71 | return None
72 | if filename not in LOOKUPS.keys():
73 | if tries >= 10:
74 | return None
75 | else:
76 | # if the filename is not found loaded into memory, it's probably being
77 | # loaded into memory right now. Try 10 more times at 100ms intervals
78 | # and if it still isn't loaded, there's likely a problem we can't fix
79 | # here.
80 | sublime.set_timeout_async(search_and_set_status_message(filename, query, panel, tries + 1), 100)
81 | else:
82 | data = LOOKUPS[filename]
83 | if len(data) > 0:
84 | matches = [item for item in data if item['name'] == query.split('.')[-1]]
85 | if len(matches) == 0:
86 | return None
87 | else:
88 | # sort matches by similarity to query
89 | matches.sort(key=lambda x: SequenceMatcher(None, query, x['fullName']).ratio(), reverse=True)
90 | item = matches[0]
91 | type_signature = item['fullName'] + ' : ' + item['signature']
92 | sublime.status_message(type_signature)
93 | panel.run_command('erase_view')
94 | # add full name and type annotation
95 | panel_output = '`' + type_signature + '`' + '\n\n' + item['comment'][1:]
96 | # replace backticks with no-width space for syntax highlighting
97 | panel_output = panel_output.replace('`', '\uFEFF')
98 | # add no-width space to beginning and end of code blocks for syntax highlighting
99 | panel_output = re.sub('\n( {4}[\s\S]+?)((?=\n\S)\n|\Z)', '\uFEFF\n\\1\uFEFF\n', panel_output)
100 | # remove first four spaces on each line from code blocks
101 | panel_output = re.sub('\n {4}', '\n', panel_output)
102 | panel.run_command('append', {'characters': panel_output})
103 | return None
104 |
105 | def get_matching_names(filename, prefix):
106 | """
107 | Given a file name and a search prefix, return a list of matching
108 | completions from elm oracle.
109 | """
110 | def skip_chars(full_name):
111 | # Sublime Text seems to have odd behavior on completions. If the full
112 | # name is at the same "path level" as the prefix, then the completion
113 | # will replace the entire entry, otherwise it will only replace after
114 | # the final period separator
115 | full_name_path = full_name.split('.')[:-1]
116 | prefix_path = prefix.split('.')[:-1]
117 | if full_name_path == prefix_path:
118 | return full_name
119 | else:
120 | # get the characters to remove from the completion to avoid duplication
121 | # of paths. If it's 0, then stay at 0, otherwise add a period back
122 | chars_to_skip = len('.'.join(prefix_path))
123 | if chars_to_skip > 0:
124 | chars_to_skip += 1
125 | return full_name[chars_to_skip:]
126 |
127 | global LOOKUPS
128 | if filename not in LOOKUPS.keys():
129 | return None
130 | else:
131 | data = LOOKUPS[filename]
132 | completions = {(v['fullName'] + '\t' + v['signature'], skip_chars(v['fullName']))
133 | for v in data
134 | if v['fullName'].startswith(prefix) or v['name'].startswith(prefix)}
135 | return [[v[0], v[1]] for v in completions]
136 |
137 | def explore_package(filename, package_name):
138 | global LOOKUPS
139 | if filename not in LOOKUPS.keys() or len(package_name) == 0:
140 | return None
141 | elif package_name[0].upper() != package_name[0]:
142 | sublime.status_message('This is not a package!')
143 | return None
144 | else:
145 | def open_link(items, i):
146 | if i == -1:
147 | return None
148 | else:
149 | open_in_browser(items[i][3])
150 | data = [[v['fullName'], v['signature'], v['comment'], v['href']]
151 | for v in LOOKUPS[filename]
152 | if v['fullName'].startswith(package_name)]
153 | # all items must be the same number of rows
154 | n = 75
155 | panel_items = [v[:2] + [v[2][:n]] + [v[2][n:2*n]] + [v[2][2*n:]] for v in data]
156 | sublime.active_window().show_quick_panel(panel_items, lambda i: open_link(data, i))
157 |
158 | def open_in_browser(url):
159 | webbrowser.open_new_tab(url)
160 |
161 | def load_from_oracle(filename):
162 | """
163 | Loads all data about the current file from elm oracle and adds it
164 | to the LOOKUPS global dictionary.
165 | """
166 | global LOOKUPS
167 | project = ElmProject(filename)
168 | if project.working_dir is None:
169 | return
170 | os.chdir(project.working_dir)
171 |
172 | # Hide the console window on Windows
173 | shell = False
174 | path_separator = ':'
175 | if os.name == "nt":
176 | shell = True
177 | path_separator = ';'
178 |
179 | settings = sublime.load_settings('Elm Language Support.sublime-settings')
180 | path = settings.get('elm_paths', '')
181 | if path:
182 | old_path = os.environ['PATH']
183 | os.environ["PATH"] = os.path.expandvars(path + path_separator + '$PATH')
184 |
185 | p = subprocess.Popen(['elm-oracle', filename, ''], stdout=subprocess.PIPE,
186 | stderr=subprocess.PIPE, shell=shell)
187 |
188 | if path:
189 | os.environ['PATH'] = old_path
190 |
191 | output, errors = p.communicate()
192 | output = output.strip()
193 | if settings.get('debug', False):
194 | string_settings = sublime.load_settings('Elm User Strings.sublime-settings')
195 | print(string_settings.get('logging.prefix', '') + '(elm-oracle) ' + str(output), '\nerrors: ' + str(errors.strip()))
196 | if str(errors.strip()):
197 | print('Your PATH is: ', os.environ['PATH'])
198 | try:
199 | data = json.loads(output.decode('utf-8'))
200 | except ValueError:
201 | return None
202 | LOOKUPS[filename] = data
203 |
204 | def view_load(view):
205 | """
206 | Selectively calls load_from_oracle based on the current scope.
207 | """
208 |
209 | if view.file_name() is None:
210 | return;
211 |
212 | sel = view.sel()[0]
213 | region = join_qualified(view.word(sel), view)
214 | scope = view.scope_name(region.b)
215 | if scope.find('source.elm') != -1:
216 | load_from_oracle(view.file_name())
217 |
218 |
219 | class ElmOracleListener(sublime_plugin.EventListener):
220 | """
221 | An event listener to load and search through data from elm oracle.
222 | """
223 |
224 | def on_selection_modified_async(self, view):
225 | sel = view.sel()[0]
226 | region = join_qualified(view.word(sel), view)
227 | scope = view.scope_name(region.b)
228 | if scope.find('source.elm') != -1:
229 | view.run_command('elm_show_type')
230 |
231 | def on_activated_async(self, view):
232 | view_load(view)
233 |
234 | def on_post_save_async(self, view):
235 | view_load(view)
236 |
237 | def on_query_completions(self, view, prefix, locations):
238 | word = get_word_under_cursor(view)
239 | return get_matching_names(view.file_name(), word)
240 |
241 |
242 | class ElmShowType(sublime_plugin.TextCommand):
243 | """
244 | A text command to lookup the type signature of the function under the
245 | cursor, and display it in the status bar if found.
246 | """
247 | type_panel = None
248 |
249 | def run(self, edit, panel=False):
250 | if self.type_panel is None:
251 | self.type_panel = self.view.window().create_output_panel('elm_type')
252 | if os.name == "nt":
253 | # using extension hide-tmLanguage because hidden-tmLanguage doesn't work correctly
254 | self.type_panel.set_syntax_file('Packages/Elm Language Support/Syntaxes/Elm Documentation.hide-tmLanguage')
255 | else:
256 | self.type_panel.set_syntax_file('Packages/Elm Language Support/Syntaxes/Elm Documentation.hidden-tmLanguage')
257 | get_type(self.view, self.type_panel)
258 | if panel:
259 | self.view.window().run_command('elm_show_type_panel')
260 |
261 |
262 | class ElmShowTypePanel(sublime_plugin.WindowCommand):
263 | """
264 | Turns on the type output panel
265 | """
266 | def run(self):
267 | self.window.run_command("show_panel", {"panel": "output.elm_type"})
268 |
269 |
270 | class ElmOracleExplore(sublime_plugin.TextCommand):
271 | def run(self, edit):
272 | word = get_word_under_cursor(self.view)
273 | parts = [part for part in word.split('.') if part[0].upper() == part[0]]
274 | package_name = '.'.join(parts)
275 | explore_package(self.view.file_name(), package_name)
276 |
277 |
278 | class EraseView(sublime_plugin.TextCommand):
279 | """
280 | Erases a view
281 | """
282 | def run(self, edit):
283 | self.view.erase(edit, sublime.Region(0, self.view.size()))
284 |
--------------------------------------------------------------------------------
/Syntaxes/Elm.tmLanguage:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | fileTypes
6 |
7 | elm
8 |
9 | name
10 | Elm
11 | patterns
12 |
13 |
14 | captures
15 |
16 | 1
17 |
18 | name
19 | punctuation.definition.entity.elm
20 |
21 | 2
22 |
23 | name
24 | punctuation.definition.entity.elm
25 |
26 |
27 | match
28 | (`)[a-zA-Z_']*?(`)
29 | name
30 | keyword.operator.function.infix.elm
31 |
32 |
33 | match
34 | \(\)
35 | name
36 | constant.language.unit.elm
37 |
38 |
39 | begin
40 | ^\b((effect|port)\s+)?(module)\s+
41 | beginCaptures
42 |
43 | 1
44 |
45 | name
46 | keyword.other.elm
47 |
48 | 3
49 |
50 | name
51 | keyword.other.elm
52 |
53 |
54 | end
55 | $|;
56 | endCaptures
57 |
58 | 1
59 |
60 | name
61 | keyword.other.elm
62 |
63 |
64 | name
65 | meta.declaration.module.elm
66 | patterns
67 |
68 |
69 | include
70 | #module_name
71 |
72 |
73 | begin
74 | (where)\s*\{
75 | beginCaptures
76 |
77 | 1
78 |
79 | name
80 | keyword.other.elm
81 |
82 |
83 | end
84 | \}
85 | patterns
86 |
87 |
88 | include
89 | #type_signature
90 |
91 |
92 |
93 |
94 | match
95 | (exposing)
96 | name
97 | keyword.other.elm
98 |
99 |
100 | include
101 | #module_exports
102 |
103 |
104 | match
105 | (where)
106 | name
107 | keyword.other.elm
108 |
109 |
110 | match
111 | [a-z]+
112 | name
113 | invalid
114 |
115 |
116 |
117 |
118 | begin
119 | ^\b(import)\s+((open)\s+)?
120 | beginCaptures
121 |
122 | 1
123 |
124 | name
125 | keyword.other.elm
126 |
127 | 3
128 |
129 | name
130 | invalid
131 |
132 |
133 | end
134 | ($|;)
135 | name
136 | meta.import.elm
137 | patterns
138 |
139 |
140 | match
141 | (as|exposing)
142 | name
143 | keyword.import.elm
144 |
145 |
146 | include
147 | #module_name
148 |
149 |
150 | include
151 | #module_exports
152 |
153 |
154 |
155 |
156 | begin
157 | (\[)(glsl)(\|)
158 | beginCaptures
159 |
160 | 1
161 |
162 | name
163 | keyword.other.elm
164 |
165 | 2
166 |
167 | name
168 | support.function.prelude.elm
169 |
170 | 3
171 |
172 | name
173 | keyword.other.elm
174 |
175 |
176 | end
177 | (\|\])
178 | endCaptures
179 |
180 | 1
181 |
182 | name
183 | keyword.other.elm
184 |
185 |
186 | name
187 | entity.glsl.elm
188 | patterns
189 |
190 |
191 | include
192 | source.glsl
193 |
194 |
195 |
196 |
197 | match
198 | \b(type alias|type|case|of|let|in|as)\s+
199 | name
200 | keyword.other.elm
201 |
202 |
203 | match
204 | \b(if|then|else)\s+
205 | name
206 | keyword.control.elm
207 |
208 |
209 | comment
210 | Floats are always decimal
211 | match
212 | \b([0-9]+\.[0-9]+([eE][+-]?[0-9]+)?|[0-9]+[eE][+-]?[0-9]+)\b
213 | name
214 | constant.numeric.float.elm
215 |
216 |
217 | match
218 | \b([0-9]+)\b
219 | name
220 | constant.numeric.elm
221 |
222 |
223 | begin
224 | """
225 | beginCaptures
226 |
227 | 0
228 |
229 | name
230 | punctuation.definition.string.begin.elm
231 |
232 |
233 | end
234 | """
235 | endCaptures
236 |
237 | 0
238 |
239 | name
240 | punctuation.definition.string.end.elm
241 |
242 |
243 | name
244 | string.quoted.double.elm
245 | patterns
246 |
247 |
248 | match
249 | \\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\'\&])
250 | name
251 | constant.character.escape.elm
252 |
253 |
254 | match
255 | \^[A-Z@\[\]\\\^_]
256 | name
257 | constant.character.escape.control.elm
258 |
259 |
260 |
261 |
262 | begin
263 | "
264 | beginCaptures
265 |
266 | 0
267 |
268 | name
269 | punctuation.definition.string.begin.elm
270 |
271 |
272 | end
273 | "
274 | endCaptures
275 |
276 | 0
277 |
278 | name
279 | punctuation.definition.string.end.elm
280 |
281 |
282 | name
283 | string.quoted.double.elm
284 | patterns
285 |
286 |
287 | match
288 | \\(NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE|DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS|US|SP|DEL|[abfnrtv\\\"'\&])
289 | name
290 | constant.character.escape.elm
291 |
292 |
293 | match
294 | \^[A-Z@\[\]\\\^_]
295 | name
296 | constant.character.escape.control.elm
297 |
298 |
299 |
300 |
301 | captures
302 |
303 | 1
304 |
305 | name
306 | punctuation.definition.string.begin.elm
307 |
308 | 2
309 |
310 | name
311 | constant.character.escape.elm
312 |
313 | 3
314 |
315 | name
316 | punctuation.definition.string.end.elm
317 |
318 |
319 | match
320 | (?x)
321 | (')
322 | (?:
323 | [\ -\[\]-~] # Basic Char
324 | | (\\(?:NUL|SOH|STX|ETX|EOT|ENQ|ACK|BEL|BS|HT|LF|VT|FF|CR|SO|SI|DLE
325 | |DC1|DC2|DC3|DC4|NAK|SYN|ETB|CAN|EM|SUB|ESC|FS|GS|RS
326 | |US|SP|DEL|[abfnrtv\\\"'\&])) # Escapes
327 | | (\^[A-Z@\[\]\\\^_]) # Control Chars
328 | )
329 | (')
330 | name
331 | string.quoted.single.elm
332 |
333 |
334 | begin
335 | ^(port\s+)?([a-z_][a-zA-Z0-9_']*|\([|!%$+\-.,=</>]+\))\s*((:)([:]+)?)
336 | beginCaptures
337 |
338 | 1
339 |
340 | name
341 | keyword.other.port.elm
342 |
343 | 2
344 |
345 | name
346 | entity.name.function.elm
347 |
348 | 4
349 |
350 | name
351 | keyword.other.colon.elm
352 |
353 | 5
354 |
355 | name
356 | invalid
357 |
358 |
359 | end
360 | $\n?
361 | name
362 | meta.function.type-declaration.elm
363 | patterns
364 |
365 |
366 | include
367 | #type_signature
368 |
369 |
370 |
371 |
372 | match
373 | \bport\s+
374 | name
375 | keyword.other.port.elm
376 |
377 |
378 | match
379 | \b[A-Z]\w*\b
380 | name
381 | constant.other.elm
382 |
383 |
384 | include
385 | #comments
386 |
387 |
388 | match
389 | ^[a-z][A-Za-z0-9_']*\s+
390 | name
391 | entity.name.function.elm
392 |
393 |
394 | include
395 | #infix_op
396 |
397 |
398 | match
399 | [|!%$?~+:\-.=</>&\\*^]+
400 | name
401 | keyword.operator.elm
402 |
403 |
404 | captures
405 |
406 | 1
407 |
408 | name
409 | support.function.delimiter.elm
410 |
411 |
412 | match
413 | ([\[\]\{\},])
414 | name
415 | constant.language.delimiter.elm
416 |
417 |
418 | match
419 | ([\(\)])
420 | name
421 | keyword.other.parenthesis.elm
422 |
423 |
424 | repository
425 |
426 | block_comment
427 |
428 | applyEndPatternLast
429 | 1
430 | begin
431 | \{-(?!#)
432 | captures
433 |
434 | 0
435 |
436 | name
437 | punctuation.definition.comment.elm
438 |
439 |
440 | end
441 | -\}
442 | name
443 | comment.block.elm
444 | patterns
445 |
446 |
447 | include
448 | #block_comment
449 |
450 |
451 |
452 | comments
453 |
454 | patterns
455 |
456 |
457 | captures
458 |
459 | 1
460 |
461 | name
462 | punctuation.definition.comment.elm
463 |
464 |
465 | match
466 | (--).*$\n?
467 | name
468 | comment.line.double-dash.elm
469 |
470 |
471 | include
472 | #block_comment
473 |
474 |
475 |
476 | infix_op
477 |
478 | match
479 | (\([|!%$+:\-.=</>]+\)|\(,+\))
480 | name
481 | entity.name.function.infix.elm
482 |
483 | module_exports
484 |
485 | begin
486 | \(
487 | end
488 | \)
489 | name
490 | meta.declaration.exports.elm
491 | patterns
492 |
493 |
494 | match
495 | \b[a-z][a-zA-Z_'0-9]*
496 | name
497 | entity.name.function.elm
498 |
499 |
500 | match
501 | \b[A-Z][A-Za-z_'0-9]*
502 | name
503 | storage.type.elm
504 |
505 |
506 | match
507 | ,
508 | name
509 | punctuation.separator.comma.elm
510 |
511 |
512 | include
513 | #infix_op
514 |
515 |
516 | comment
517 | So named because I don't know what to call this.
518 | match
519 | \(.*?\)
520 | name
521 | meta.other.unknown.elm
522 |
523 |
524 |
525 | module_name
526 |
527 | match
528 | [A-Z][A-Za-z0-9._']*
529 | name
530 | support.other.module.elm
531 |
532 | type_signature
533 |
534 | patterns
535 |
536 |
537 | captures
538 |
539 | 1
540 |
541 | name
542 | entity.other.inherited-class.elm
543 |
544 | 2
545 |
546 | name
547 | variable.other.generic-type.elm
548 |
549 | 3
550 |
551 | name
552 | keyword.other.big-arrow.elm
553 |
554 |
555 | match
556 | \(\s*([A-Z][A-Za-z]*)\s+([a-z][A-Za-z_']*)\)\s*(=>)
557 | name
558 | meta.class-constraint.elm
559 |
560 |
561 | match
562 | ->
563 | name
564 | keyword.other.arrow.elm
565 |
566 |
567 | match
568 | =>
569 | name
570 | keyword.other.big-arrow.elm
571 |
572 |
573 | match
574 | \b[a-z][a-zA-Z0-9_']*\b
575 | name
576 | variable.other.generic-type.elm
577 |
578 |
579 | match
580 | \b[A-Z][a-zA-Z0-9_']*\b
581 | name
582 | storage.type.elm
583 |
584 |
585 | match
586 | \(\)
587 | name
588 | support.constant.unit.elm
589 |
590 |
591 | include
592 | #comments
593 |
594 |
595 |
596 |
597 | scopeName
598 | source.elm
599 | uuid
600 | 2cb90e5e-6e98-456d-9a8a-b59935dbc4b0
601 |
602 |
603 |
--------------------------------------------------------------------------------