Full Code of sagold/FuzzyFilePath for AI

master 5c50ecdc2af2 cached
77 files
90.1 KB
23.4k tokens
194 symbols
1 requests
Download .txt
Repository: sagold/FuzzyFilePath
Branch: master
Commit: 5c50ecdc2af2
Files: 77
Total size: 90.1 KB

Directory structure:
gitextract_1a51ib0w/

├── .gitignore
├── .npmignore
├── CHANGES.md
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── Default.sublime-keymap
├── FuzzyFilePath.md
├── FuzzyFilePath.sublime-settings
├── LICENSE
├── Main.sublime-menu
├── ProjectListener.py
├── QueryCompletionListener.py
├── README.md
├── TestRunner.py
├── ViewListener.py
├── command_goto_file.py
├── command_insert_path.py
├── command_rebuild_cache.py
├── command_replace_region.py
├── command_show_context.py
├── command_show_current_settings.py
├── command_show_info.py
├── common/
│   ├── __init__.py
│   ├── config.py
│   ├── path.py
│   ├── selection.py
│   ├── settings.py
│   ├── string.py
│   └── verbose.py
├── completion.py
├── controller.py
├── current_state.py
├── expression.py
├── project/
│   ├── FileCache.py
│   ├── FileCacheWorker.py
│   ├── __init__.py
│   └── validate.py
├── query.py
└── test/
    ├── __init__.py
    ├── integration/
    │   ├── __init__.py
    │   ├── get_context_test.py
    │   ├── tests.py
    │   └── tools.py
    ├── mock/
    │   └── project/
    │       ├── app/
    │       │   ├── boot.js
    │       │   ├── index.html
    │       │   ├── latex.tex
    │       │   ├── src/
    │       │   │   ├── coffee/
    │       │   │   │   ├── coffee.coffee
    │       │   │   │   ├── module.coffee
    │       │   │   │   └── other.coffee
    │       │   │   ├── common.js
    │       │   │   ├── main.js
    │       │   │   ├── tools.js
    │       │   │   └── ts/
    │       │   │       ├── another.ts
    │       │   │       ├── module.ts
    │       │   │       ├── other.ts
    │       │   │       ├── tsconfig.json
    │       │   │       └── typescript.ts
    │       │   └── styles/
    │       │       ├── index.css
    │       │       ├── partials/
    │       │       │   ├── common.scss
    │       │       │   └── media.scss
    │       │       └── styles.css
    │       └── bower_components/
    │           ├── angular/
    │           │   └── index.js
    │           └── lib/
    │               ├── component/
    │               │   ├── component.html
    │               │   ├── component.scss
    │               │   ├── componentCtrl.js
    │               │   └── componentModel.js
    │               ├── css/
    │               │   ├── index.css
    │               │   └── styles.css
    │               └── index.js
    ├── tools.py
    └── unit/
        ├── __init__.py
        ├── debug_test.py
        ├── get_closest_folder_test.py
        ├── query_test.py
        └── tests.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
/temp
*.pyc
*.psd
FuzzyFilePath.sublime-workspace
FuzzyFilePath.sublime-project


================================================
FILE: .npmignore
================================================
/test
*.gif

================================================
FILE: CHANGES.md
================================================
19/04/03

- Update triggers for sass, js and ts

18/05/06

- Add `additional_scopes` list for custom scope extension
- Update edit_settings command
- Add json as valid js extension

17/04/01

- fixed an error while post-replacing inserted filepath, which exited with a broken result

CHANGES

- improved goto-file command by quering a simplified path (webpack ~)
- improved goto-file command to open uncached files if filepath does exist
- filecache worker now includes subfolders defined as project-folder, even if exclude_folders does match them


16/09/10

- support for multiple folders
- bugfixes
- major code simplifications


15/06/07

- fixed project settings to be added on top of default settings
- fixed update settings for modified user settings
- fixed an issue on windows where paths were not inserted relative


15/03

- major refactoring and bugfixes

FEATURES

- open path under cursor
- show popup containing context evaluation information, required to setup triggers (requires Sublime Text build 3073)
- set project specifiv project_directory via command palette
- after completion insertion, move to beginning of next word
- support multiple opened projects (in separate windows)

CHANGES

- improve update of caching
	- update cache on project change
	- update cache window focus
	- add command to rebiuld cache manually
	- update cache if a new file is saved
- improve replacement of current path fragments
- display of file path suggestions will not separate file extension. this allows querying filetype


14/11/23

BREAKING CHANGES

- **change** `exclude_folders` items to be matched as regex
- **remove** `extensionsToSuggest`, now being retrieved from scope settings
- **remove** shortcut `super+ctrl+space`
- **remove** option `auto_trigger`
- **remove** option `insertExtension`. Should now be done by `replace_on_insert`
- **change** and extend default scope rules
- **change** absolute paths to start with "/name"
- **change** path prefix (./, ../, /) now overwrites scope settings

CHANGES

- extend insert\_path command (shortcut) with base\_directory and extensions
- add support for base_directory
- extend scope-rules by tagName, style and prefix
- analyse context of cursor to retrieve tagName, style and prefix
- improve retrieval of query in view
- fix path replacement for files being in root
- stability improvements
- refactorings

14/11/02

- fix bug in instant completions (missing string-cleanup method)
- fix trigger to update cached files
- remove bugged merge of exlude\_folder\_patterns and exlude\_folders. Thus exlude\_folders are always applied
- fix bug in updating cache (triggered for invalid file extensions)
- refactorings

14/10/03

- extend insert\_path command by replace\_on\_insert
- add FuzzyFilePath Package Settings to Sublime Text Menu
- setting replace\_on\_insert
- setting disable\_keymap\_actions
- setting disable_autocompletions
- add integration tests

================================================
FILE: Default (Linux).sublime-keymap
================================================
[
    {
        "keys": ["ctrl+alt+space"],
        "command": "insert_path",
        "args": {
            "type": "relative"
        }
    },

    {
        "keys": ["ctrl+shift+space"],
        "command": "insert_path",
        "args": {
            "type": "absolute"
        }
    },

    {
        "keys": ["alt+enter"],
        "command": "ffp_goto_file"
    }
]

================================================
FILE: Default (OSX).sublime-keymap
================================================
[
    {
        "keys": ["ctrl+alt+space"],
        "command": "insert_path",
        "args": {
            "type": "relative"
        }
    },

    {
        "keys": ["ctrl+shift+space"],
        "command": "insert_path",
        "args": {
            "type": "absolute"
        }
    },

    {
        "keys": ["alt+enter"],
        "command": "ffp_goto_file"
    }
]

================================================
FILE: Default (Windows).sublime-keymap
================================================
[
    {
        "keys": ["ctrl+alt+space"],
        "command": "insert_path",
        "args": {
            "type": "relative"
        }
    },

    {
        "keys": ["ctrl+shift+space"],
        "command": "insert_path",
        "args": {
            "type": "absolute"
        }
    },

    {
        "keys": ["alt+enter"],
        "command": "ffp_goto_file"
    }
]

================================================
FILE: Default.sublime-commands
================================================
[
	{ "caption": "FuzzyFilePath: Rebuild cache", "command": "ffp_update_cache" },
	{ "caption": "FuzzyFilePath: Show info", "command": "ffp_show_info" },
	{ "caption": "FuzzyFilePath: Set project directory", "command": "ffp_set_project_directory" },
	{ "caption": "FuzzyFilePath: Show current settings", "command": "ffp_show_current_settings" },
	{ "caption": "FuzzyFilePath: Goto File", "command": "ffp_goto_file"},
	{ "caption": "FuzzyFilePath: Insert Relative Path", "command": "insert_path", "args": {
          "type": "relative"
        }
    },
	{ "caption": "FuzzyFilePath: Insert Absolute Path", "command": "insert_path", "args": {
          "type": "absolute"
        }
    }
]


================================================
FILE: Default.sublime-keymap
================================================
[
    {
        "keys": ["ctrl+alt+space"],
        "command": "insert_path",
        "args": {
            "type": "relative"
        }
    },

    {
        "keys": ["ctrl+shift+space"],
        "command": "insert_path",
        "args": {
            "type": "absolute"
        }
    },

    {
        "keys": ["alt+enter"],
        "command": "ffp_goto_file"
    }
]

================================================
FILE: FuzzyFilePath.md
================================================
# FuzzyFilePath - autocomplete filepaths

@version 0.6.1
@author Sascha Goldhofer <post@saschagoldhofer.de>

## tasks

- possibly create file caches of all project directories simultaneously
- Cleanup @TODO flags
- suddenly Testrunner causes plugin host to expire

### bugs

    - Initial opened view may be falsely recognised as "not within project"

### release

    - WIP: Test changes
    - DONE: Test windows paths
    - Add documentation for multiple folder support

### performance

    - searching in large folders, where the query matches a folder containing many files, are very slow. i.e. searching for "node_modules/path/to/package" is much slower than searching for "path/to/package" (filecount 10k+)
        - current workaround fast_query option in default settings
        - so far the regex can not be improved to be faster and still return the same results, the first step should be to exclude unused folders which (may require an option for folder whitelisting) i.e. "node_modules/(?!szig).*"

### features

    - growing list of triggers is getting unmaintainable
        - Probably group by main-scope in object for faster retrieval and namespacing
        - create an object with ids for a specific trigger and use a list of ids for triggers to use (selecting object)
        - further support trigger objects in the scope-list
    - add custom triggers without overriding the default scopes

### ideas

    - maybe support different triggers based on inserted filepath? (i.e. absolute if matches node_mod...)
    - possibly send ffp states to serve for better debugging


================================================
FILE: FuzzyFilePath.sublime-settings
================================================
{
	// set project_directory relative to sublime project directory
	"project_directory": "",
	// base directory for paths relative to project_directory. Used if scope-trigger contains "base_directory": true
	// watch out for side effects with project directory (if base_directory is not within project_directory)
	"base_directory": false,
	// disable automatic path completions
	"disable_autocompletions": false,
	// disable keymaps
	"disable_keymap_actions": false,
	// ignore folders that match following regular expressions
	"exclude_folders": ["node_modules", "bower_components"],
	// logs scope evaluation to console to debug configuration
	"log": false,
	// LIST OF TRIGGERS FOR AUTO COMPLETION
	// - setting "scopes" in user settings will override all other scopes.
	// - create "additional_scopes" in user settings to add more scopes instead.
	//   they will be added after the existing scopes.
	// - triggers are evaluated in given order. First match wins
	// - specialize trigger by 1. scope 2. prefix, style and/or tagname
	// - command (insert_path) still requires a trigger
	"scopes": [
		// MINIMAL AUTO TRIGGER - NOT RECOMMENDED
		// will always query files for auto completions
		// {
		// 	"scope": ".",
		// 	"extensions": ["*"],
		// 	"auto": true
		// },
		/*
			Javascript
		 */
		{
			// js: require()
			"scope": "\\.(js|ts)\\s",					// 1. ignore if scope at cursor does not match expression (super+alt+p)
			"prefix": ["require", "define"],		// 2. trigger only if: require(<cursor>, define([<cursor>
			// if 1 & 2 are true:
			"auto": true,
			"relative": true,						// insert absolute
			"base_directory": false,				// insert absolute from the set base directory (above)
			"extensions": ["ts", "js", "html", "scss", "json"],	// show only .js, .html and .scss files
			"replace_on_insert": [
				["\\.(js|ts)$", ""],						// after insertion, remove .js from path
				["([^.])\\/index$", "\\1"],			// nodejs will load index.js by default => also remove index
				// remove path to module-folder, since our build tool resolves this path automatically
				["^([\\/.]+)?\\/(bower_components|node_modules)\\/", ""]
			]
		},
		{
			// es6 import from/import "*"
			"scope": "string.*\\.(js|ts)\\s",

			"auto": true,
			"relative": true,
			"base_directory": false,
			"prefix": ["from", "import"],
			"extensions": ["ts", "js", "html", "scss", "json"],
			"replace_on_insert": [
				["\\.(js|ts)$", ""],
				["([^.])\\/index$", "\\1"],
				["^([\\/.]+)?\\/(bower_components|node_modules)\\/", ""]
			]
		},
		{
			// Typescript // <reference path="" />
			"scope": "source\\.ts\\scomment",

			"auto": true,
			"relative": true,
			"base_directory": false,
			"prefix": ["path"],
			"tagName": ["reference"],
			"extensions": ["ts", "js"],
			"replace_on_insert": []
		},
		{
			// Typescript es6 import from/import "*"
			"scope": "string[^\\s]*\\.ts\\s",

			"auto": true,
			"relative": true,
			"base_directory": false,
			"prefix": ["from", "import"],
			"extensions": ["js", "ts"],
			"replace_on_insert": [
				["(\\.js|\\.ts)$", ""],
				["([^.])\\/index$", "\\1"],
				["^(\\/.+)?\\/(bower_components|node_modules)\\/", ""]
			]
		},
		{
			// Coffeescript es6 import from/import "*" and require
			"scope": "string[^\\s]*\\.coffee\\s",

			"auto": true,
			"relative": true,
			"base_directory": false,
			"prefix": ["from", "import", "require"],
			"extensions": ["coffee"],
			"replace_on_insert": []
		},
		{
		    // js - *.src = ""
		    "scope": "source\\.js.*string",
		    "prefix": ["src"],

		    "auto": true,
		    "base_directory": true,
		    "extensions": ["png", "gif", "jpg", "jpeg"],
		    "relative": true
		},
		/*
			CSS
		 */
		{
 			// import ""
			"scope": "source\\.(css|sass|scss|less)",

			"auto": true,
			"prefix": ["import"],
			"relative": true,
			"extensions": ["css", "sass", "scss", "less"],
			"replace_on_insert": [
				["\\.(css|scss)$", ""],
				["^(\\/.+)?\\/(bower_components|node_modules)\\/", ""]
			]
		},
		{
		    // *: url()
		    "scope": "source\\.(css|sass|less)",
		    "prefix": ["url"],

		    "auto": true,
		    "relative": true,
		    "base_directory": true,
		    "extensions": ["png", "gif", "jpeg", "jpg", "woff", "ttf", "svg", "otf"],
		    "replace_on_insert": [
		    	["\\.(css|scss)$", ""],
		    	["^(\\/.+)?\\/(bower_components|node_modules)\\/", ""]
		    ]
		},
		/*
			HTML
		 */
		{
		    // html - <script src="..">
		    "scope": "string\\.quote.*\\.html",
		    "prefix": ["src"],
		    "tagName": ["script"],
		    "auto": true,
		    "base_directory": false,
		    "extensions": ["js"],
		    "relative": true
		},
		{
			// string.quote.double.html
		    // html - <img src="" and <script src="" and <* style="*: url()
		    "scope": "text\\.html",
		    // <* src
		    "prefix": ["src", "url"],

		    "auto": true,
		    "base_directory": true,
		    "extensions": ["js", "png", "gif", "jpeg", "jpg", "svg"],
		    "relative": true
		},
		{
		    // html - <link href=""
		    "scope": "meta\\.tag.*string.*\\.html",
		    // "scope": "html",
		    // <link href
		    "tagName": ["link"],
		    "prefix": ["href"],

		    "auto": true,
		    "extensions": ["css"],
		    "relative": true
		},
		/*
			OTHER
		 */
		{
			// glsl
			"scope": "source\\.glsl",
			"prefix": ["import"],
			"auto": true,
			"extensions": ["glsl"],
			"relative": true,
			"replace_on_insert": [
				["\\.glsl$", ""]
			]
		},
		{
			// handlebars partial
			"scope": "\\.handlebars\\s.*inline",

			"auto": true,
			"relative": true,
			"base_directory": false,
			"extensions": ["hbs", "handlebars"],
			"replace_on_insert": [
				["^(\\/.+)?\\/(bower_components|node_modules)\\/", ""]
			]
		},
	    {
			// python
			"scope": "\\.python",
			"extensions": ["py"],
			"auto": true,
			"prefix": ["from"],
			"relative": false,
			"replace_on_insert": [
				["^\\/", ""],
				["\\/", "."],
				// ! remove
				["^(.*)\\.py$", "FuzzyFilePath.\\1"]
			]
		},
		{
			// php
			"auto": true,
			"scope": "\\.php",
			"extensions": ["php"],
			"prefix": ["require_once", "include"],
			"relative": false,
			"replace_on_insert": [["^\\/", ""]]
		},
		{
		    // latex \input{}
		    "scope": "meta\\.include\\.latex",
		    "prefix": ["input"],

		    "auto": true,
		    "relative": true,
		    "base_directory": false,
		    "extensions": ["png", "gif", "jpeg", "jpg", "svg"]
		},
		{
		    // * - any source file (by command only)
		    "scope": "source",

		    "auto": false,
		    "extensions": ["js", "html", "css", "scss", "less", "png", "gif", "jpeg", "jpg", "svg"],
		    "relative": true
		},
	],
	"additional_scopes": [
		{

		}
	]
}


================================================
FILE: LICENSE
================================================

                     DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
                             Version 2, December 2004

          Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>

          Everyone is permitted to copy and distribute verbatim or modified
          copies of this license document, and changing it is allowed as long
          as the name is changed.

                     DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
            TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

           0. You just DO WHAT THE FUCK YOU WANT TO.


================================================
FILE: Main.sublime-menu
================================================
[
    {
        "caption": "Preferences",
        "mnemonic": "n",
        "id": "preferences",
        "children":
        [
            {
                "caption": "Package Settings",
                "mnemonic": "P",
                "id": "package-settings",
                "children":
                [
                    {
                        "caption": "FuzzyFilePath",
                        "children":
                        [
                            {
                                "caption": "Settings",
                                "command": "edit_settings", "args":
                                {
                                    "base_file": "${packages}/FuzzyFilePath/FuzzyFilePath.sublime-settings",
                                    "default": "{\n\t$0\n}\n"
                                }
                            },
                            { "caption": "-" },
                            {
                                "caption": "Key Bindings",
                                "command": "edit_settings", "args":
                                {
                                    "base_file": "${packages}/FuzzyFilePath/Default ($platform).sublime-keymap",
                                    "default": "[\n\t$0\n]\n"
                                }
                            }
                        ]
                    }
                ]
            }
        ]
    }
]


================================================
FILE: ProjectListener.py
================================================
import sublime
import sublime_plugin

import FuzzyFilePath.controller as controller


ID = "ProjectListener"


class ProjectListener(sublime_plugin.EventListener):
    """ listens on window changes, delegating events to controller """

    previous_project = None
    previous_window = None

    def on_activated(self, view):
        window = view.window()
        if not window:
            return False

        project_id = get_project_id(window)

        if self.previous_project != project_id:
            if self.previous_project is not None:
                self.on_project_activated(view)
            self.previous_project = project_id

        elif self.previous_window is not sublime.active_window().id():
            self.previous_window = sublime.active_window().id()
            self.on_window_activated(view)

    # project has been refocused
    def on_window_activated(self, view):
        controller.on_project_focus(view.window())

    # another (possible) project has been opened/focused
    def on_project_activated(self, view):
        window = view.window()
        if not window:
            return False
        controller.on_project_activated(window)


def get_project_id(window):
    project_name = window.project_file_name()
    if project_name:
        return project_name
    return window.id()


================================================
FILE: QueryCompletionListener.py
================================================
import re
import sublime_plugin
import FuzzyFilePath.controller as controller
import FuzzyFilePath.common.selection as Selection
import FuzzyFilePath.expression as Context
import FuzzyFilePath.common.settings as settings
from FuzzyFilePath.common.verbose import verbose
import FuzzyFilePath.current_state as state


ID = "QueryCompletionListener"


class QueryCompletionListener(sublime_plugin.EventListener):

    # tracks on_post_insert_completion
    track_insert = {
        "active": False,
        "start_line": "",
    }
    post_remove = ""

    def on_query_completions(self, view, prefix, locations):
        if settings.get("disable_autocompletion") and not Query.by_command():
            return False

        if self.track_insert["active"] is False:
            self.start_tracking(view)

        completions = controller.get_filepath_completions(view)
        if completions is not False:
            return completions

        self.finish_tracking(view)
        return False

    #custom
    def on_post_insert_completion(self, view, command_name):
        controller.on_query_completion_inserted(view, self.post_remove)

    #custom
    def on_query_abort(self):
        controller.on_query_completion_aborted()

    # track post insert insertion
    def start_tracking(self, view, command_name=None):
        if not state.is_valid():
            return

        self.track_insert["active"] = True
        self.track_insert["start_line"] = Selection.get_line(view)
        """
            - sublime inserts completions by replacing the current word
            - this results in wrong path insertions if the query contains word_separators like slashes
            - thus the path until current word has to be removed after insertion
            - ... and possibly afterwards
        """
        context = Context.get_context(view)
        needle = context.get("needle")
        word = re.escape(Selection.get_word(view))
        self.post_remove = re.sub(word + "$", "", needle)
        verbose(ID, "start tracking", self.post_remove)

    def finish_tracking(self, view, command_name=None):
        self.track_insert["active"] = False
        verbose(ID, "finish tracking")

    def abort_tracking(self):
        self.track_insert["active"] = False
        verbose(ID, "abort tracking")

    def on_text_command(self, view, command_name, args):
        # check if a completion may be inserted
        if command_name in settings.get("trigger_action", []) or command_name in settings.get("insert_action", []):
            self.start_tracking(view, command_name)
        elif command_name == "hide_auto_complete":
            self.on_query_abort()
            self.abort_tracking()

    # check if a completion is inserted and trigger on_post_insert_completion
    def on_post_text_command(self, view, command_name, args):
        if len( view.sel() ) < 1:
            return
        current_line = Selection.get_line(view)
        command_trigger = command_name in settings.get("trigger_action", []) and self.track_insert["start_line"] != current_line
        if command_trigger or command_name in settings.get("insert_action", []):
            self.finish_tracking(view, command_name)
            self.on_post_insert_completion(view, command_name)


================================================
FILE: README.md
================================================
# [FuzzyFilePath](https://github.com/sagold/FuzzyFilePath)

__Sublime Text Plugin__

Fuzzy search and insert filenames inside your current project directory. Highly customizable.

<img src="https://raw.githubusercontent.com/sagold/FuzzyFilePath/develop/FuzzyFilePathDemo.gif" />
<br />
<em style="display: block; text-align: right;">Basic settings support Javascript, HTML, CSS, PHP and glsl, but may be
adjusted for most languages</em>


## <a name="installation">Installation</a>


### [Package Control](https://sublime.wbond.net/)

After [Package Control installation](https://sublime.wbond.net/installation), restart Sublime Text. Use the Command Palette <kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (OS X) or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (Linux/Windows) and search for *Package Control: Install Package*. Wait until Package Control downloaded the latest package list and search for *FuzzyFilePath*.


### [github](https://github.com/sagold/FuzzyFilePath.git)

in `<SublimeConfig>/Packages/` call: `git clone https://github.com/sagold/FuzzyFilePath.git`

__Sublime Text 2__

in `<SublimeConfig>/Packages/FuzzyFilePath/` switch to Sublime Text 2 Branch with: `git checkout st2`

Attention: Sublime Text 2 will no longer be supported.



## <a name="usage">Usage</a>

**Filepaths will be suggested if there is a matching
[trigger](https://github.com/sagold/FuzzyFilePath/wiki/Settings#trigger) for the current context** and its property
_auto_ is set to _true_. For a matching [trigger](https://github.com/sagold/FuzzyFilePath/wiki/Settings#trigger),
filepath completions may be forced (ignoring _auto_ property) by the following shorcuts:

- <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Space</kbd> inserts filepaths relative, overriding possible settings
- <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Space</kbd> inserts filepaths absolute, overriding possible settings

The current string may modify the suggested filepaths by the following rules:

- `word` suggests all matching files by the type (relative or absolute) as specified in the matched rule
- `./` suggests matching files within the current directory and inserts selection relative
- `../` suggests all matching files and inserts selection relative
- `/folder` suggests all matching files and insert selection absolute

FuzzyFilePath is disabled for single files or files outside the opened folder.


### Open File

Use <kbd>Alt</kbd>+<kbd>Enter</kbd> to open the file under cursor


### Configure Completion Panel

Ensure you have [autocompletion activated for Sublime](https://www.granneman.com/webdev/editors/sublime-text/top-features-of-sublime-text/auto-completion-in-sublime-text/). In those cases, where the autocompletion panel is still
not opened (for any type of completions), you may extend `auto_complete_triggers` to add special rules for the
completion panel to show up. i.e. enabling autocompletion for latex `\input{"path/to/asset"}`, you could add:

```json
"auto_complete_triggers":
[
	{
		"characters": "abcdefghijklmnopqrstuvwxyz",
		"selector": "text.tex.latex"
	}
]
```

or enabling html completion for `<script src="path/to/script">`

```json
"auto_complete_triggers":
[
	{
		"characters": "abcdefghijklmnopqrstuvwxyz",
		"selector": "string.quoted.double.html"
	}
]
```


### Special Characters

If your projects contains filenames with special characters, consider modifying Sublime Texts `word_separators`.

i.e. in AngularJs filenames may start with `$`. In _Sublime Text | Preferences | Settings - User_ redeclare word
separators, removing `$`:
```js
	"word_separators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?"
```


## Customization

For further details about troubleshooting, customization, settings and keybindings please
[refer to the Wiki](https://github.com/sagold/FuzzyFilePath/wiki)

Trying to integrate other languages? See the
[auto complete Python package tutorial](https://github.com/sagold/FuzzyFilePath/wiki/Tutorial:-Add-support-for-python-packages)


#### Related Plugins

##### [AutoFileName](https://github.com/BoundInCode/AutoFileName)

- uses file discovery based on current directory instead of fuzzy search
- adds properties for images in autocompletion description







================================================
FILE: TestRunner.py
================================================
"""
	FuzzyFilePath unit and integration test runner

	Usage:
		Bind a shortcut like
		{ "keys": ["super+y"], "command": "ffp_test_runner" }
		to execute tests. Make sure a default view is selected (not console)

"""
import sublime
import sublime_plugin
import traceback

from FuzzyFilePath.test.unit.tests import tests as unitTests
from FuzzyFilePath.test.integration.tests import tests as integrationTests
from FuzzyFilePath.test.integration.tools import ViewHelper

LINE = "----------------"


class FfpTestRunner(sublime_plugin.TextCommand):

	tools = None,

	def console_blur(self):
		window = sublime.active_window()
		window.focus_group(0)
		window.focus_view(window.active_view())

	def console_open(self):
		window = sublime.active_window()
		window.run_command("show_panel", {"panel": "console", "toggle": False})
		self.console_blur()

	def after(self):
		self.tools.move_cursor(0, 0)
		# delete text
		selection = self.tools.view.sel()[0]
		position = selection.begin()
		region = self.tools.view.line(position)
		self.tools.view.erase(self.tools.edit, region)

	def setUp(self, edit):
		self.console_blur()
		window = sublime.active_window()
		self.tools = ViewHelper(window, window.new_file(), edit)

	def closeView(self): {
		self.tools.view.close()
	}

	def tearDown(self, failed_tests, total_tests):
		# self.tools.view.close()
		print(LINE)
		if failed_tests > 0:
			notice = "{0} of {1} tests failed".format(failed_tests, total_tests)
			print(notice)
			sublime.status_message("FFP Intergration: " + notice)
		else:
			notice = "{0} tests successful".format(total_tests)
			print(notice)
			sublime.status_message("FFP Intergration: " + notice)

	def run(self, edit):
		print("\nFuzzyFilePath Integration Tests")
		total_tests = 0
		failed_tests = 0

		# run unit tests
		for testCase in unitTests:
			total_tests += testCase.length
			for should in testCase.tests:
				test = getattr(testCase, should)

				if "before_each" in dir(testCase):
					testCase.before_each()

				try:
					test()
				except:
					failed_tests += 1
					print("\n" + testCase.name + " " + should + ":")
					print(LINE)
					traceback.print_exc()
					print(LINE)
					print()
					pass

				if "after_each" in dir(testCase):
					testCase.after_each()

		# run integration tests
		for testCase in integrationTests:
			self.setUp(edit)
			total_tests += testCase.length

			for should in testCase.tests:
				test = getattr(testCase, should)

				if "before_each" in dir(testCase):
					testCase.before_each(self.tools)

				try:
					test(self.tools)
				except:
					failed_tests += 1
					print("\n" + testCase.name + " " + should + ":")
					print(LINE)
					traceback.print_exc()
					print(LINE)
					print()
					pass

				if "after_each" in dir(testCase):
					testCase.after_each(self.tools)

				if not testCase.unit_test:
					self.after()

				self.closeView()

		self.tearDown(failed_tests, total_tests)

		if failed_tests > 0:
			self.console_open()


================================================
FILE: ViewListener.py
================================================
import re
import copy
import sublime_plugin

import FuzzyFilePath.controller as controller
import FuzzyFilePath.current_state as state


temp_views = []


def is_valid(view):
    if view.file_name():
        return True
    if not view.id() in temp_views:
        temp_views.append(view.id())
    return False


class ViewListener(sublime_plugin.EventListener):
    def on_post_save_async(self, view):
        if view.id() in temp_views:
            self.on_file_created(view)
            self.on_activated(view)

    def on_activated(self, view):
        # view has gained focus
        if is_valid(view):
            controller.on_file_focus(view)

    def on_file_created(self, view):
        temp_views.remove(view.id())
        controller.on_file_created()


================================================
FILE: command_goto_file.py
================================================
import os
import re
import sublime
import sublime_plugin
import FuzzyFilePath.expression as Context
import FuzzyFilePath.common.path as Path
from FuzzyFilePath.common.verbose import log
import FuzzyFilePath.common.selection as Selection
import FuzzyFilePath.current_state as state


ID = "GotoFile"


class FfpGotoFileCommand(sublime_plugin.TextCommand):
    """ open file under cursor """
    def run(self, edit):
        current_directory = os.path.dirname(self.view.file_name())
        context = Context.get_context(self.view)
        if context.get("valid") is False:
            return log(ID, "abort, no valid path given:", context.get("needle"))

        path = context.get("needle")
        project_folder = state.get_project_directory()

        if not (path and project_folder):
            return log(ID, "path or project invalid", path, project_folder)

        is_relative = Path.is_relative(path)
        if is_relative:
            path = Path.get_absolute_path(current_directory, path)
            path = re.sub(project_folder, "", path)


        path = re.sub("^[\\\\/\.]", "", path)
        realpath = path
        # cleanup string, in case there are special characters for a path
        # e.g. webpack uses ~, which is not part of path
        path = re.sub("[^A-Za-z0-9 \-_\\\\/.%?#]*", "", path)
        files = state.find_file(path)

        if len(files) == 0:
            # it may be an uncached file, try to open it by using the real path
            if self.filepath_exists(os.path.join(project_folder, realpath)):
                return self.open_file(project_folder, realpath)
            return log(ID, "failed finding file", path, "it is not within the cache and not a realpath")

        if len(files) == 1:
            self.open_file(project_folder, files[0])
        else:
            # if javascript, search for index.js
            current_scope = self.view.scope_name(Selection.get_position(self.view))
            if re.search("\.js ", current_scope):
                for file in files:
                    if "index.js" in file:
                        return self.open_file(project_folder, file)

            self.files = files
            self.project_folder = project_folder
            self.view.show_popup_menu(files, self.select_file)

    def filepath_exists(self, filepath):
        return os.path.isfile(filepath)

    def select_file(self, index):
        self.open_file(self.project_folder, self.files[index])
        self.files = None
        self.project_folder = None

    def open_file(self, project_folder, filepath):
        path = os.path.join(project_folder, filepath)
        sublime.active_window().open_file(path)



================================================
FILE: command_insert_path.py
================================================
import sublime_plugin
import FuzzyFilePath.common.settings as settings
import FuzzyFilePath.query as Query


class InsertPathCommand(sublime_plugin.TextCommand):
    """ trigger customized autocomplete overriding auto settings """
    def run(self, edit, type="default", base_directory=None, replace_on_insert=[], extensions=[]):
        if settings.get("DISABLE_KEYMAP_ACTIONS") is True:
            return False

        Query.override_trigger_setting("filepath_type", type)
        Query.override_trigger_setting("base_directory", base_directory)

        if len(replace_on_insert) > 0:
            Query.override_trigger_setting("replace_on_insert", replace_on_insert)
        if len(extensions) > 0:
            Query.override_trigger_setting("extensions", extensions)

        self.view.run_command('auto_complete', "insert")


================================================
FILE: command_rebuild_cache.py
================================================
import sublime
import sublime_plugin
import FuzzyFilePath.current_state as state


class FfpUpdateCacheCommand(sublime_plugin.TextCommand):
    """ force update project-files cache """
    def run(self, edit):
    	for folder in sublime.active_window().folders():
        	state.rebuild_filecache(folder)


================================================
FILE: command_replace_region.py
================================================
import re
import sublime
import sublime_plugin
import FuzzyFilePath.common.settings as settings


class FfpReplaceRegionCommand(sublime_plugin.TextCommand):

    # helper: replaces range with string
    def run(self, edit, a, b, string, move_cursor=False):
        if settings.get("DISABLE_KEYMAP_ACTIONS") is True:
            return False

        self.view.replace(edit, sublime.Region(a, b), string)

        if move_cursor and settings.get("POST_INSERT_MOVE_CHARACTERS"):
        	self.move_skip(a + len(string))

    def move_skip(self, point):
    	length = 0
    	word_region = self.view.word(point)
    	line_region = self.view.line(point)
    	post_region = sublime.Region(word_region.b, line_region.b)
    	post = self.view.substr(post_region)
    	to_move = re.search(settings.get("POST_INSERT_MOVE_CHARACTERS"), post)

    	if to_move:
    		length = len(to_move.group(0))

    	self.move_cursor(point + length)

    def move_cursor(self, point):
    	self.view.sel().clear()
    	self.view.sel().add(sublime.Region(point))
    	self.view.window().focus_view(self.view)
    	self.view.run_command('_enter_insert_mode') # vintageous


================================================
FILE: command_show_context.py
================================================
import sublime_plugin

import FuzzyFilePath.expression as Context
import FuzzyFilePath.common.selection as Selection
from FuzzyFilePath.completion import resolve_trigger
import FuzzyFilePath.query as Query


class FfpShowContextCommand(sublime_plugin.TextCommand):
	""" displays the current context, required for debugging and trigger setup """

	content = None

	def run(self, edit):
		context = Context.get_context(self.view)
		current_scope = self.view.scope_name(Selection.get_position(self.view))
		trigger = resolve_trigger(self.view, Query)
		print("FOUND TRIGGER", trigger)

		self.content = "<h4>FuzzyFilepath - context evaluation</h4>"
		self.add("filepath valid", context.get("valid_needle"))
		self.add("context valid", context.get("is_valid"))
		self.content += "<br>"
		self.add("path", context.get("needle"))
		self.add("prefix", context.get("prefix"))
		self.add("property", context.get("style"))
		self.add("tag", context.get("tagName"))
		self.add("word", context.get("word"))
		self.content += "<br>"
		self.add("scope", current_scope)
		self.content += "<br>"
		self.content += "<h5>Trigger</h5>"
		if trigger is False:
			self.content += "no trigger could be found matching the given context"
		else:
			for key in trigger:
				self.add(key, trigger.get(key))

		self.show()

	def add(self, label, value):
		value = "-" if value is None else value
		self.content += "<div><b>" + label + ": </b>" + str(value) + "</div>"

	def show(self):
		self.view.show_popup(self.content)


================================================
FILE: command_show_current_settings.py
================================================
import json
import sublime
import sublime_plugin
import FuzzyFilePath.common.settings as settings


class FfpShowCurrentSettingsCommand(sublime_plugin.TextCommand):
    """ shows a message dialog with project validation status of current file """
    def run(self, edit):
        json_string = json.dumps(settings.current(), indent=4, sort_keys=True)
        new_file = sublime.active_window().new_file()
        new_file.insert(edit, 0, json_string)
        new_file.set_syntax_file("Packages/JavaScript/JavaScript.tmLanguage")


================================================
FILE: command_show_info.py
================================================
import sublime_plugin
import FuzzyFilePath.common.settings as settings
import FuzzyFilePath.project.validate as Validate


class FfpShowInfoCommand(sublime_plugin.TextCommand):
    """ shows a message dialog with project validation status of current file """
    def run(self, edit):
        Validate.view(self.view, settings.current(), True)


================================================
FILE: common/__init__.py
================================================


================================================
FILE: common/config.py
================================================
config = {

	"debug": False,
	"log": False,
	"id": "config",
	"ffp_settings_file": "FuzzyFilePath.sublime-settings",
	"escape_dollar": '\$',
	"trigger_action": ["auto_complete", "insert_path"],
	"insert_action": ["commit_completion", "insert_best_completion"],
	"trigger_statements": ["prefix", "tagName", "style"],
	"fast_query": False,

	"base_directory": False,
	"project_directory": False,
	"disable_autocompletion": False,
	"disable_keymap_actions": False,
	"auto_trigger": True,
	"trigger": [],
	"additional_scopes": [],
	"exclude_folders": ["node\\_modules", "bower\\_components/.*/bower\\_components"],

	"post_insert_move_characters": "^[\"\'\);]*"
}


================================================
FILE: common/path.py
================================================
import re
import os

def sanitize(path):
    # sanitize slashes (posix)
    path = posix(path)
    # sanitize ././
    path = re.sub("^\/?(./)+", "./", path)
    path = re.sub("^(\/)+", "/", path)
    return path

def posix(path):
    if path is not None:
        path = path.replace("\\", "/")
    return path

def is_relative(string):
    return bool(re.match("(\.?\.\/)", string))

def is_absolute(string):
    return bool(re.match("\/[A-Za-z0-9\_\-\s\.$]*", string))

def sanitize_base_directory(path):
    path = sanitize(path)
    path = os.path.dirname(path)
    # no leading nor trailing slash
    path = re.sub("^\/*", "", path)
    path = re.sub("\/*$", "", path)
    return path

def get_absolute_path(base_path, relative_path):
    # return absolute target of join(base_path, relative_path)
    # http://stackoverflow.com/questions/17295086/python-joining-current-directory-and-parent-directory-with-os-path-join?rq=1
    path = os.path.join(base_path, relative_path)
    path = os.path.abspath(path)
    return path

def get_relative_folder(file_name, base_directory):
    folder = os.path.dirname(file_name)
    folder = os.path.relpath(folder, base_directory)
    folder = "" if folder == "." else folder
    return sanitize(folder)

def relative_to(base_directory, folder_path):
    folder = os.path.relpath(folder_path, base_directory)
    return sanitize(folder)

# return {string} path from base to target
# !replace with `os.path.relpath(path[, start])`
# => https://docs.python.org/2/library/os.path.html
def trace(from_folder, to_folder):
    if not from_folder:
        return sanitize("./" + to_folder)

    bases = from_folder.split("/")
    targets = to_folder.split("/")
    result = ""
    index = 0

    # step back base, until in same folder
    size = min(len(bases), len(targets))
    while (index < size and bases[index] == targets[index]):
        index += 1

    # strip common folders
    del bases[0:index]
    del targets[0:index]

    if (len(bases) == 0):
        # from base path?
        result = './'
    else:
        result = "../" * len(bases)

    result += "/".join(targets)
    # !Do Debug "//"
    result = re.sub("//", "/", result);

    return result


================================================
FILE: common/selection.py
================================================

def get_position(view):
	selection = view.sel()
	return selection[0].begin() if selection else ""

def get_line(view):
    position = get_position(view)
    line_region = view.line(position)
    return view.substr(line_region)

def get_word(view):
    word_region = view.word(get_position(view))
    return view.substr(word_region)

def get_scope(view):
    return view.scope_name(get_position(view))


================================================
FILE: common/settings.py
================================================
"""
    manages the current setting data which is retrieved in the following order (bottom to top)
    - project settings: folder settings
    - project settings
    - user settings file
    - default settings file
    - config
"""
import sublime
import os
import FuzzyFilePath.common.path as Path
from FuzzyFilePath.common.config import config

# base settings: config, default, user settings file
base_settings = {}
# project settings merged with base settings
project_settings = {}
# final settings object
current_settings = {}
# backwards compatibility of setting schema
map_settings = {
    "trigger": "scopes"
}


def get(key, default=None):
    return current_settings.get(key.lower(), default)


def current():
    return current_settings


def update():
    """ merges plugin settings with user settings by default """
    update_base_settings()
    update_project_settings()


def update_base_settings():
    global base_settings, project_settings, current_settings
    base_settings = get_base_settings(config)
    project_settings = base_settings
    current_settings = base_settings
    #verbose("BASE_SETTINGS", current_settings)


# @TODO improve memory
def update_project_settings():
    global base_settings, project_settings, current_settings
    project_settings = get_project_settings(base_settings)
    current_settings = project_settings
    #verbose("PROJECT_SETTINGS", current_settings)


def update_project_folder_settings(project_folder):
    global project_settings, current_settings
    current_settings = get_folder_settings(project_settings, project_folder)
    #verbose("CURRENT_SETTINGS", current_settings)


def get_project_settings(base):
    """ returns project settings """
    data = sublime.active_window().project_data()
    if not data:
        return base
    ffp_project_settings = data.get("settings", {}).get("FuzzyFilePath", {})
    ffp_project_settings = sanitize(ffp_project_settings)
    return merge(base, ffp_project_settings)


def get_base_settings(config):
    user_settings = sublime.load_settings(config["ffp_settings_file"])
    # Note: user_settings is of class Settings
    user_settings = merge(config, user_settings)
    user_settings = merge_scopes(user_settings)
    return sanitize(user_settings)


def get_folder_settings(project, project_folder=None):
    if not project_folder:
        return project

    folder_settings = get_folder_setting(project_folder)
    folder_settings = sanitize(folder_settings)
    return merge(project, folder_settings)


def merge(settings, overwrite={}):
    """ merge settings object with given overwrite settings """
    result = {}
    for key in settings:
        result[key] = overwrite.get(key.lower(), settings.get(key))
    # backwards compatibility
    for key in map_settings:
        mappedKey = map_settings[key]
        result[key] = overwrite.get(mappedKey, settings.get(key))

    return result


def merge_scopes(settings):
    """Merge triggers from 'additional_scopes' in user settings to the main triggers, if present"""
    triggers = settings.get("trigger")
    additional_scopes = settings.get("additional_scopes", [])
    for trigger in additional_scopes:
        triggers.append(trigger)
    settings["trigger"] = triggers
    return settings


# @TODO improve memory
def get_folder_setting(folder=None):
    """ returns the project config object FuzzyFilePath associated with the given folder """
    if not folder:
        return {}
    data = sublime.active_window().project_data()
    if not data:
        return {}
    folders = data.get("folders")
    if not folders:
        return {}
    # if the project file has been saved, paths in project settings may be relative, but the given filepath is absolute
    # (retrieved from window.folder())
    project_directory = sublime.active_window().project_file_name()
    if project_directory:
        project_directory = os.path.dirname(project_directory)
        folder = Path.relative_to(project_directory, folder)

    for folder_settings in folders:
        if folder_settings.get("path") == folder:
            settings = folder_settings.get("FuzzyFilePath", {})
            verbose("found folder settings", folder, ":", settings)
            return settings

    verbose("no folder settings found", folder)
    return {}


def sanitize(settings_object):
    if "base_directory" in settings_object and settings_object.get("base_directory"):
        settings_object["base_directory"] = Path.sanitize_base_directory(settings_object.get("base_directory"))
    if "project_directory" in settings_object and settings_object.get("project_directory"):
        settings_object["project_directory"] = Path.sanitize_base_directory(settings_object.get("project_directory"))
    return settings_object


def verbose(*args):
    if get("log") is True:
        print("FFP\t", "Settings", *args)


================================================
FILE: common/string.py
================================================
def get_diff(first, second):
    # get intersection at start
    start = get_start_diff(first, second)

    # remove intersection to prevent end diff duplicates
    first = first[len(start):]
    second = second[len(start):]
    end = get_end_diff(first, second)

    return {
        "start": start,
        "end": end
    }

def get_start_diff(first, second):
    index = 0
    result = ""
    for c in first:
        if c is second[index]:
            index += 1
            result += c
        else:
            break

    return result

def get_end_diff(first, second):
    first = first[::-1] # reverse string
    second = second[::-1]
    second_length = len(second)
    index = 0
    result = ""
    for c in first:
        if index < second_length and c is second[index]:
            index += 1
            result = c + result
        else:
            break

    return result


================================================
FILE: common/verbose.py
================================================
import FuzzyFilePath.common.settings as settings

IGNORE = ["CurrentFile", "QueryCompletionListener", "search", "Expression"]

def log(*args):
	if settings.get("log"):
		print("FFP\t", *args)

def verbose(*args):
    if settings.get("debug") is True and not args[0] in IGNORE:
        print("FFP\t", *args)

def warn(*args):
	print("FFP -WARNING-\t", *args)

def start_block():
	if settings.get("log") or settings.get("debug"):
		print("")

def end_block():
	if settings.get("log") or settings.get("debug"):
		print("")


================================================
FILE: completion.py
================================================
"""
    Manage active state of current completion and post cleanup
"""
import re
import sublime
import FuzzyFilePath.expression as Context
import FuzzyFilePath.common.path as Path
from FuzzyFilePath.common.string import get_diff
import FuzzyFilePath.common.selection as Selection
from FuzzyFilePath.common.verbose import log
from FuzzyFilePath.common.verbose import verbose
import FuzzyFilePath.common.settings as settings
import FuzzyFilePath.current_state as current_state


ID = "Completion"
ID_TRIGGER = "SelectedTrigger"

start_expression = False
scope_cache = {}

state = {
    "active": False,            # completion currently in progress (serve suggestions)
    "onInsert": [],             # substitutions for building final path
    "base_directory": False     # base directory to set for absolute path
}


def start(post_replacements=[]):
    state["replace_on_insert"] = post_replacements
    state["active"] = True


def stop():
    state["active"] = False
    state["base_directory"] = False


def is_active():
    return state.get("active")


def resolve_trigger(view, query):
    global start_expression

    # parse current context, may contain 'is_valid: False'
    start_expression = Context.get_context(view)
    if start_expression["error"] and not query.by_command():
        verbose(ID, "abort - not a valid context")
        return False

    current_scope = Selection.get_scope(view)
    trigger = find_trigger(current_scope, start_expression, query.by_command())

    # currently trigger is required in Query.build
    if trigger is False:
        verbose(ID, "abort - no trigger found")
        return False

    return trigger


def get_filepaths(view, query):
    global start_expression

    trigger = resolve_trigger(view, query)
    log(ID_TRIGGER, trigger)
    if query.build(start_expression.get("needle"), trigger, current_state.get_directory()) is False:
        # query is valid, but may not be triggered: not forced, no auto-options
        verbose(ID, "abort - no auto trigger found")
        return False

    # remembed the path for `update_inserted_filepath`, query will be reset...
    state["base_directory"] = query.get_post_remove_path()

    return current_state.search_completions(
        query.get_needle(),
        current_state.get_project_directory(),
        query.get_extensions(),
        query.get_base_path()
    )


def find_trigger(current_scope, expression, byCommand=False):
    """ Returns the first trigger matching the given scope and expression """
    triggers = settings.get("TRIGGER")
    if not byCommand:
        # get any triggers that match the requirements and may start automatically
        triggers = get_matching_autotriggers(current_scope, settings.get("TRIGGER"))
    if not bool(triggers):
        verbose(ID, "abort query, no valid scope-regex for current context")
        return False
    # check if one of the triggers match the current context (expression, scope)
    return Context.find_trigger(expression, current_scope, triggers)


def update_inserted_filepath(view, post_remove):
    """ post completion: adjusts inserted filepath """
    expression = Context.get_context(view)
    # diff of previous needle and inserted needle
    diff = get_diff(post_remove, expression["needle"])
    # cleanup string
    result = re.sub("^" + diff["start"], "", expression["needle"])
    # do not replace current word
    if diff["end"] != start_expression["word"]:
        result = re.sub(diff["end"] + "$", "", result)

    # remove path query completely
    result = apply_post_replacements(result, state.get("base_directory"), state.get("replace_on_insert"))
    log("post cleanup path:'", expression.get("needle"), "' ~~> '", result, "'")

    # replace current query with final path
    view.run_command("ffp_replace_region", {
        "a": expression["region"].a,
        "b": expression["region"].b,
        "string": result,
        "move_cursor": True
    })


def get_matching_autotriggers(scope, triggers):
    """ Returns all triggers that match the given scope """
    # get cached evaluation
    result = scope_cache.get(scope)
    if result is None:
        # evaluate triggers on current scope
        result = [trigger for trigger in triggers if trigger.get("auto") and re.search(trigger.get("scope"), scope)]
        # add to cache
        scope_cache[scope] = result if len(result) > 0 else False
        result = scope_cache.get(scope)

    return result


def apply_post_replacements(path, base_directory, replace_on_insert):
    # hack reverse
    path = re.sub(settings.get("ESCAPE_DOLLAR"), "$", path)
    for replace in replace_on_insert:
        path = re.sub(replace[0], replace[1], path)

    if base_directory and path.startswith("/"):
        path = re.sub("^\/" + base_directory, "", path)
        path = Path.sanitize(path)

    return path


================================================
FILE: controller.py
================================================
import sublime
import FuzzyFilePath.completion as Completion
import FuzzyFilePath.query as Query
import FuzzyFilePath.common.settings as Settings
from FuzzyFilePath.common.verbose import verbose
from FuzzyFilePath.common.verbose import log
import FuzzyFilePath.current_state as state


ID = "Controller"


#init
def plugin_loaded():
    """ load settings """
    update_settings()
    settings_file = sublime.load_settings(Settings.get("ffp_settings_file"))
    settings_file.add_on_change("update", update_settings)


def update_settings():
    """ restart projectFiles with new plugin and project settings """
    # invalidate cache
    global scope_cache
    scope_cache = {}
    Settings.update()
    state.enable()
    state.update_settings()


#query
def get_filepath_completions(view):
    if not state.is_valid():
        Query.reset()
        return False

    verbose(ID, "get filepath completions")
    completions = Completion.get_filepaths(view, Query)

    if completions and len(completions[0]) > 0:
        Completion.start(Query.get_replacements())
        view.run_command('_enter_insert_mode') # vintageous
        log("{0} completions found".format(len(completions)))
    else:
        if Query.get_needle() is not None:
            sublime.status_message("FFP no filepaths found for '" + Query.get_needle() + "'")
        Completion.stop()

    Query.reset()
    return completions


def on_query_completion_inserted(view, post_remove):
    if Completion.is_active():
        verbose(ID, "query completion inserted")
        Completion.update_inserted_filepath(view, post_remove)
        Completion.stop()


def on_query_completion_aborted():
    Completion.stop()


#project
def on_project_focus(window):
    """ window has gained focus, rebuild file cache (in case files were removed/added) """
    verbose(ID, "refocus project")
    for folder in window.folders():
            state.rebuild_filecache(folder)


def on_project_activated(window):
    """ a new project has received focus """
    verbose(ID, "activate project")
    state.update()



#file
def on_file_created():
    """ a new file has been created """
    verbose(ID, "file created -- rebuild cache")
    state.update()
    state.rebuild_filecache()


def on_file_focus(view):
    state.update()


================================================
FILE: current_state.py
================================================
"""
	manages current state, which include: current filename of view, current project folder if any, project folder
	does not manage settings, but sends a message with the current project folder to settings
"""
ID = "CurrentState"


import sublime
import os
import re
import FuzzyFilePath.common.settings as settings
from FuzzyFilePath.project.FileCache import FileCache
import FuzzyFilePath.common.path as Path
import FuzzyFilePath.common.verbose as logger


is_enabled = False # set to true when plugin has initially updated settings
valid = False # if the current view is a valid project file
file_caches = {} # caches any file indices of each project folder
state = {} # saves current views state like filename, project_folder, cache and settings


def update():
	""" call me anytime a new view has gained focus. This includes activation of a new window, which should have an
		active view
	"""
	global valid, is_enabled

	if not is_enabled:
		return False

	temp = False
	window = sublime.active_window()
	if window is None:
		logger.log(ID, "Abort -- no active window")
		valid = False
		return valid
	view = window.active_view()
	if view is None:
		logger.log(ID, "Abort -- no active view")
		valid = False
		return valid
	file = Path.posix(view.file_name())
	if not file:
		logger.log(ID, "Abort -- view has not yet been saved to file")
		temp = True
		return valid
	if state.get("file") == file:
		logger.log(ID, "Abort -- view already updated")
		return valid

	folders = list(map(lambda f: Path.posix(f), window.folders()))
	project_folder = get_closest_folder(file, folders)

	if project_folder is False:
		logger.log(ID, "Abort -- file not part of a project (folder)")
		valid = False
		return valid

	# notify settings of new project folder
	if state.get("project_folder") != project_folder:
		settings.update_project_settings()
	settings.update_project_folder_settings(project_folder)

	valid = True

	# @TODO cache
	state["file"] = file
	state["directory"] = sanitize_directory(file, project_folder)
	state["folders"] = folders
	state["project_folder"] = project_folder
	state["cache"] = get_file_cache(project_folder)

	logger.start_block()
	logger.verbose(ID, "Updated", state)

	return valid


def sanitize_directory(file_name, project_folder):
	directory = re.sub(project_folder, "", file_name)
	directory = re.sub("^[\\\\/\.]*", "", directory)
	return os.path.dirname(directory)


def get_project_directory():
	return state.get("project_folder")


def get_directory():
	return state.get("directory")


def update_settings():
	if state.get("project_folder"):
		# we expect settings to be already updated and thus only update the project folder settings
		settings.update_project_folder_settings(state.get("project_folder"))


def is_valid():
	return valid


def enable():
	global is_enabled
	is_enabled = True


def get_file_cache(folder):
	if not folder in file_caches:
		valid_file_extensions = get_valid_extensions(settings.get("trigger"))
		logger.verbose(ID, "Build cache for " + folder + " (", valid_file_extensions , ") excluding", settings.get("exclude_folders"))
		file_caches[folder] = FileCache(valid_file_extensions, settings.get("exclude_folders"), folder)

	return file_caches.get(folder)


def rebuild_filecache(folder=None):
	if not folder:
		if state.get("cache"):
			logger.verbose(ID, "rebuild current filecache of folder " + state.get("project_folder"))
			state.get("cache").rebuild()
		return

	folder = Path.posix(folder)
	if not folder in file_caches:
		logger.log(ID, "Abort rebuild filecache -- folder " + folder + " not cached")
		return False

	logger.verbose(ID, "rebuild current filecache of folder " + folder)
	file_caches.get(folder).rebuild()


def search_completions(needle, project_folder, valid_extensions, base_path=False):
	return state.get("cache").search_completions(needle, project_folder, valid_extensions, base_path)


def find_file(file_name):
	return state.get("cache").find_file(file_name)


def get_valid_extensions(triggers):
	""" Returns a list of all file extensions found in scope triggers """
	extensionsToSuggest = []
	for scope in triggers:
	    ext = scope.get("extensions", [])
	    extensionsToSuggest += ext
	# return without duplicates
	return list(set(extensionsToSuggest))


def get_closest_folder(filepath, directories):
	"""
		Returns the (closest) project folder associated with the given file or False

		# the rational behind this is as follows:
		In nodejs we might work with linked node_modules. Each node_module is a separate project. Adding nested folders
		to the root document thus owns the file and defines the project scope. A separated folder should never reach
		out (via files) on its parents folders.
	"""
	folderpath = os.path.dirname(filepath)
	current_folder = folderpath
	closest_directory = False
	for folder in directories:
		distance = current_folder.replace(folder, "")
		if len(distance) < len(folderpath):
			folderpath = distance
			closest_directory = folder
	return closest_directory


================================================
FILE: expression.py
================================================
import re
import sublime
import FuzzyFilePath.common.settings as settings
import FuzzyFilePath.common.selection as Selection
import FuzzyFilePath.common.verbose as logger

ID = "Expression"

NEEDLE_SEPARATOR = ">\"\'\(\)\{\}"
NEEDLE_SEPARATOR_BEFORE = "\"\'\(\{"
NEEDLE_SEPARATOR_AFTER = "^\"\'\)\}"
NEEDLE_CHARACTERS = "\.A-Za-z0-9\-\_$"
NEEDLE_INVALID_CHARACTERS = "\"\'\)=\:\(<>\n\{\}"
DELIMITER = "\s\:\(\[\=\{"

def get_context(view):
	error = False
	valid = True
	valid_needle = True
	position = Selection.get_position(view)

	# regions
	word_region = view.word(position)
	line_region = view.line(position)
	pre_region = sublime.Region(line_region.a, word_region.a)
	post_region = sublime.Region(word_region.b, line_region.b)

	# text
	line = view.substr(line_region)
	word = view.substr(word_region)
	pre = view.substr(pre_region)
	post = view.substr(post_region)

	error = re.search("[" + NEEDLE_INVALID_CHARACTERS + "]", word)

	needle_region = view.word(position)

	# grab everything in 'separators'
	needle = ""
	separator = False
	pre_match = ""
	# search for a separator before current word, i.e. <">path/to/<position>
	pre_quotes = re.search("(["+NEEDLE_SEPARATOR_BEFORE+"])([^"+NEEDLE_SEPARATOR+"]*)$", pre)
	if pre_quotes:
		needle += pre_quotes.group(2) + word
		separator = pre_quotes.group(1)
		pre_match = pre_quotes.group(2)
		needle_region.a -= len(pre_quotes.group(2))
	else:
		# use whitespace as separator
		pre_quotes = re.search("(\s)([^"+NEEDLE_SEPARATOR+"\s]*)$", pre)
		if pre_quotes:
			needle = pre_quotes.group(2) + word
			separator = pre_quotes.group(1)
			pre_match = pre_quotes.group(2)
			needle_region.a -= len(pre_quotes.group(2))

	if pre_quotes:
		post_quotes = re.search("^(["+NEEDLE_SEPARATOR_AFTER+"]*)", post)
		if post_quotes:
			needle += post_quotes.group(1)
			needle_region.b += len(post_quotes.group(1))
		else:
			logger.verbose(ID, "no post quotes found => invalid")
			valid = False
	elif not re.search("["+NEEDLE_INVALID_CHARACTERS+"]", needle):
		needle = pre + word
		needle_region.a = pre_region.a
	else:
		needle = word

	# grab prefix
	prefix_region = sublime.Region(line_region.a, pre_region.b - len(pre_match) - 1)
	prefix_line = view.substr(prefix_region)
	# # print("prefix line", prefix_line)

	#define? (["...", "..."]) -> before?
	# before: ABC =:([
	prefix = re.search("\s*(["+NEEDLE_CHARACTERS+"]+)["+DELIMITER+"]*$", prefix_line)
	if prefix is None:
		# validate array, like define(["...", ".CURSOR."])
		prefix = re.search("^\s*(["+NEEDLE_CHARACTERS+"]+)["+DELIMITER+"]+", prefix_line)

	if prefix:
		# print("prefix:", prefix.group(1))
		prefix = prefix.group(1)

	tag = re.search("<\s*(["+NEEDLE_CHARACTERS+"]*)\s*[^>]*$", prefix_line)
	if tag:
		tag = tag.group(1)
		# print("tag:", tag)

	propertyName = re.search("[\s\"\'']*(["+NEEDLE_CHARACTERS+"]*)[\s\"\']*\:[^\:]*$", prefix_line)
	if propertyName:
		propertyName = propertyName.group(1)
		# print("style:", style)

	if separator is False:
		logger.verbose(ID, "separator undefined => invalid", needle)
		valid_needle = False
		valid = False
	elif re.search("["+NEEDLE_INVALID_CHARACTERS+"]", needle):
		logger.verbose(ID, "invalid characters in needle => invalid", needle)
		valid_needle = False
		valid = False
	elif prefix is None and separator.strip() == "":
		logger.verbose(ID, "prefix undefined => invalid", needle)
		valid = False

	return {
		"is_valid": valid,
		"valid_needle": valid_needle,
		"needle": needle,
		"prefix": prefix,
		"tagName": tag,
		"style": propertyName,
		"region": needle_region,
		"word": word,
		# really do not use any of this
		"error": error
	}

def check_trigger(trigger, expression):
	# returns True if the expression statements match the trigger
	for statement in set(settings.get("trigger_statements")).intersection(trigger):
		values = trigger.get(statement)
		# statement values may be None (or any other value...)
		if type(values) is list and not expression.get(statement) in values:
			return False
		# validate other value by comparison
		# elif not values == expression.get(statement):
		# 	return False

	return True

def find_trigger(expression, scope, triggers):
	for trigger in triggers:
		# if the trigger is defined for the current scope
		# REQUIRED? scope = properties.get("scope").replace("//", "")
		if re.search(trigger["scope"], scope):
			# validate its statements on the current context
			if check_trigger(trigger, expression):
				return trigger

	return False

def get_rule(view):

	selection = view.sel()[0]
	position = selection.begin()
	word_region = view.word(position)

	current_scope = view.scope_name(word_region.a)
	context = get_context(view)
	rule = find_rule(context, current_scope)

	return [rule, context] if rule else False


================================================
FILE: project/FileCache.py
================================================
import sublime
import os
import gc
import re
from FuzzyFilePath.common.verbose import verbose
from FuzzyFilePath.common.verbose import log
import FuzzyFilePath.common.path as Path
import FuzzyFilePath.common.settings as settings
from FuzzyFilePath.project.FileCacheWorker import FileCacheWorker

ID = "search"
ID_CACHE = "cache"

class FileCache:
    """
        Manages path suggestions by loading, caching and filtering project files. Add folders by
        `add(<path_to_parent_folder>)`
    """

    def __init__(self, file_extensions, exclude_folders, directory):
        self.directory = directory
        self.valid_extensions = file_extensions
        self.exclude_folders = exclude_folders
        self.cache = None

        self.rebuild()

    def update_settings(self, file_extensions, exclude_folders):
        settings_have_changed = self.valid_extensions != file_extensions or self.exclude_folders != exclude_folders
        self.valid_extensions = file_extensions
        self.exclude_folders = exclude_folders
        if settings_have_changed:
            self.rebuild()

    def search_completions(self, needle, project_folder, valid_extensions, base_path=False):
        """
            retrieves a list of valid completions, containing fuzzy searched needle

            Parameters
            ----------
            needle : string -- to search in files
            project_folder : string -- folder to search in, cached via add
            valid_extensions : array -- list of valid file extensions
            base_path : string -- of current file, creates a relative path if not False
            with_extension : boolean -- insert extension

            return : List -- containing sublime completions
        """
        log(ID, needle, project_folder, valid_extensions, base_path)
        project_files = self.cache.files
        if (project_files is None):
            return False

        # basic: strip any dots
        needle = re.sub("\.\./", "", needle)
        needle = re.sub("\.\/", "", needle)
        # remove starting slash
        needle = re.sub("^\/", "", needle)
        # cleanup
        needle = re.sub('["\'\(\)$]', '', needle)
        # prepare for regex extension string
        needle = re.escape(needle);

        # build search expression
        if settings.get("fast_query"):
            regex = ".*" + re.sub("\\\/", ".*\\\/.*", needle) + ".*"
        else:
            regex = ".*"
            for i in needle:
                regex += i + ".*"

        verbose(ID, "scan", len(project_files), "files for", regex, "(" + needle + ")", valid_extensions);

        # get matching files
        result = []
        for filepath in project_files:
            properties = project_files.get(filepath)
            """
                properties[0] = escaped filename without extension, like "test/mock/project/index"
                properties[1] = file extension, like "html"
                properties[2] = file displayed as suggestion, like 'test/mock/project/index     html'
            """
            """
                funny: the regex matched here is also matched again by Sublime itself. thus returning all
                filepaths is as good as filtering them before, with the exception that the first one is incredibly
                faster...

                The problem with the second solution is, that sublime does not include anything outside word separators
                and thus proposing many invalid paths. Thus the regex has to be faster, by either
                - omitting anything after the last slash, since sublime does the rest correctly (no improvement)
                - splitting the search by folders...
            """
            if ((properties[1] in valid_extensions or "*" in valid_extensions) and re.match(regex, filepath, re.IGNORECASE)):
            # if ((properties[1] in valid_extensions or "*" in valid_extensions)):
                completion = self.get_completion(filepath, base_path)
                result.append(completion)

        return (result, sublime.INHIBIT_EXPLICIT_COMPLETIONS | sublime.INHIBIT_WORD_COMPLETIONS)

    def find_file(self, file_name):
        project_files = self.cache.files
        if (project_files is None):
            return False

        result = []
        file_name_query = ".*" + re.escape(file_name) + ".*"
        for filepath in project_files:
            if re.match(file_name_query, filepath, re.IGNORECASE):
                result.append(filepath)
        return result

    def get_completion(self, target_path, base_path=False):
        if base_path is False:
            # absolute path
            return (target_path, "/" + target_path)
        else:
            # create relative path
            return (target_path, Path.trace(base_path, target_path))

    def file_is_cached(self, file_name):
        """ returns False if the given file is not within cache
            tests files with full path or relative from project directory
        """
        name, extension = os.path.splitext(file_name)
        extension = extension[1:]
        if not extension in self.valid_extensions:
            verbose(ID_CACHE, "file to cache has no valid extension", extension)
            return True

        file_name = re.sub(self.directory, "", file_name)
        return self.cache.get(file_name, False) is not False


    def rebuild(self):
        self.cache = FileCacheWorker(self.exclude_folders, self.valid_extensions, self.directory)
        self.cache.start();


================================================
FILE: project/FileCacheWorker.py
================================================
"""
    Scans, parses and stores all files in the given folder to the dictionary `files`

    Each file entry is set by its relative `filepath` and holds an array like
        0 : filename (modified)
        1 : file extension
        2 : sublime text auto completion string
"""
import sublime
import os
import re
import threading
from FuzzyFilePath.common.verbose import verbose
import FuzzyFilePath.common.settings as settings


ID = "cache"


def posix(path):
    return path.replace("\\", "/")

# stores all files and its fragments within property files
class FileCacheWorker(threading.Thread):

    def __init__(self, exclude_folders, extensions, folder):
        threading.Thread.__init__(self)

        self.exclude_folders = exclude_folders
        self.extensions = extensions
        self.folder = folder
        self.files = None

    def run(self):
        verbose(ID, "START adding files in", self.folder)
        self.files = self.read(self.folder)
        #verbose(ID, len(self.files), "files cached")
        print("FuzzyFilePath cached {0} files in {1}".format(len(self.files), self.folder))


    def read(self, folder, base=None):
        """return all files in folder"""
        folder_cache = {}
        base = base if base is not None else folder

        if base is not folder:
            # ensure project_folders are not excluded by another folders exclude patterns
            # e.g. project/node_modules excludes project/node_modules/module, which is also a project folder
            relative_folder = os.path.relpath(folder, base)
            # test ignore expressions on current path
            for test in self.exclude_folders:
                if re.search(test, relative_folder) is not None:
                    # verbose(ID, "skip " + folder)
                    return folder_cache

        # ressources =
        for ressource in os.listdir(folder):
            current_path = os.path.join(folder, ressource)

            if (os.path.isfile(current_path)):

                relative_path = os.path.relpath(current_path, base)
                filename, extension = os.path.splitext(relative_path)
                extension = extension[1:]

                # posix required for windows, else absolute paths are wrong: /asd\ads\
                relative_path = posix(relative_path)
                # substitute $ which prevents errors in further processing. is replaced again in completion.py post repl
                relative_path = re.sub("\$", settings.get("escape_dollar"), relative_path)

                if extension in self.extensions:
                    current_filename = posix(filename)
                    folder_cache[relative_path] = [
                        # modified filepath. $ hack is reversed in post_commit_completion
                        re.sub("\$", settings.get("escape_dollar"), current_filename),
                        # extension of file
                        extension,
                        # sublime completion text
                        current_filename + "\t" + extension
                    ]

            elif (not ressource.startswith('.') and os.path.isdir(current_path)):
                folder_cache.update(self.read(current_path, base))

        return folder_cache


================================================
FILE: project/__init__.py
================================================


================================================
FILE: project/validate.py
================================================
"""
    Validates the current file`s folders based on project folders and its settings.
    Reminder: project = sublime window and opened folder (sidebar)

    Either returns False, if no completions are possible or a dictionary containing required paths and folders of the
    project, file and completion base directory. These folders are required to build the required path completions.
"""
import re
import os
import sublime

import FuzzyFilePath.common.path as Path
from FuzzyFilePath.common.verbose import log


def view(view, config, open_dialog=False):
    """
        returns sanitized directories of current file

        Parameters
        ----
        view : Sublime.View     -- view of current file
        open_dialog: Boolean    -- optional: show directory information in dialog

        return : Dictionary     -- containing evaluted projects path, where
            "project": String       modified project directory from settings:project_directory
            "base": String          relative folder in project from which to resolve paths to files
            "current" : String      relative folder in project directory of current file
    """
    if (file_has_location(view) is False):
        return show_dialog("disabled: file is temporary", open_dialog)

    if (is_project() is False):
        return show_dialog("disabled: not a project", open_dialog)

    directory = project_directory(view, config["PROJECT_DIRECTORY"])
    if directory is False:
        return show_dialog("\ndisabled:{0}<div>not within project directory {1}"
                .format(view.file_name(), config["PROJECT_DIRECTORY"]), open_dialog)

    config["BASE_DIRECTORY"] = sanitize_base_directory(config["BASE_DIRECTORY"], directory["project"], directory["base"])
    directory["current"] = get_current_folder_relative(view, directory["project"])

    if open_dialog is True:
        message = "\nproject directory:\n'{0}'\n".format(directory["project"])
        message += "\nbase directory: '{0}'\n".format(config["BASE_DIRECTORY"])
        message += "\ncurrent directory: '{0}'\n".format(directory["current"])
        show_dialog(message, open_dialog)

    return directory


def get_current_folder_relative(view, project_directory):
    return Path.get_relative_folder(view.file_name(), project_directory)


def file_has_location(view):
    return view.file_name() is not None


def is_project():
    return sublime.active_window().folders() is not None


def get_valid_path(string):
    return re.sub("^[\\\/\.]*", "", string)


def project_directory(view, project_directory):
    """
        Get evaluated project folder of file

        Parameters
        ----
        view : Sublime.View         -- current view/file
        project_directory : String  -- directory of current view

        return : Dictionary         -- False or dictionary with folders as
    """
    directory = {
        "settings": "",     # specific project directory (relative) given in settings
        "base": False,      # basepath of project (absolute)
        "project": False    # final project path extended by settings: project_directory
    }

    if project_directory:
        # strip any path characters up front, else path.join fails
        directory["settings"] = get_valid_path(project_directory)

    file_name = view.file_name()
    for folder in sublime.active_window().folders():
        # find and build current project directory
        directory["project"] = os.path.join(folder, directory["settings"])
        if directory["project"] in file_name:
            is_project_file = True
            directory["base"] = folder
            return directory
            break

    return False


def sanitize_base_directory(base_directory, project_directory, base_project_directory):
    """
        validate possible base directory
    """
    if not base_directory:
        return ""

    base_directory = get_valid_path(base_directory)
    # base_project_directory    | /path/to/sublime/project
    # project_directory         | /path/to/sublime/project/project_directory
    # - path_to_base_directory  | /path/to/sublime/project/base_directory
    # + path_to_base_directory  | /path/to/sublime/project/project_directory/base_directory
    path_to_base_directory = os.path.join(project_directory, base_directory)
    if not os.path.isdir(path_to_base_directory):
        # BASE_DIRECTORY is NOT a valid folder relative to (possibly modified) project_directory
        path_to_base_directory = os.path.join(base_project_directory, base_directory)

        if not os.path.isdir(path_to_base_directory):
            log("Error: setting's base_directory is not a valid directory in project")
            log("=> changing base_directory {0} to ''".format(base_directory))
            return ""

        elif path_to_base_directory in project_directory:
            # change BASE_DIRECTORY to be '' since its outside of project directory
            log("Error: setting's base_directory is within project directory")
            log("=> changing base_directory {0} to ''".format(base_directory))
            return ""

        else:
            # change BASE_DIRECTORY to be relative to modified project directory
            path_to_base_directory = path_to_base_directory.replace(project_directory, "")
            log("Error: setting's base_directory is not relative to project directory")
            log("=> changing base_directory '{0}' to '{1}'".format(base_directory, path_to_base_directory))
            return get_valid_path(path_to_base_directory)

    return base_directory


def show_dialog(message, open=False):
    if open is True:
       header = "FuzzyFilePath\n\n"
       sublime.message_dialog(header + message)
    return False


================================================
FILE: query.py
================================================
"""
    Build current query based on received modifiers
"""
import re
import FuzzyFilePath.common.path as Path
from FuzzyFilePath.common.verbose import log
import FuzzyFilePath.common.settings as settings


state = {

    "extensions": ["*"],
    "base_directory": False,    # path of current query
    "post_remove_path": False   # path to remove on post cleanup
}

# set by insert_path_command: overrideable properties for next query
# "extensions": [],
# "filepath_type": False,
# "base_path": "",
# "replace_on_insert": []
override = {}


def reset():
    state["extensions"] = ["*"]
    state["base_directory"] = False
    state["replace_on_insert"] = []
    override.clear()


def override_trigger_setting(key, value):
    override[key] = value


def by_command():
    return bool(override.get("filepath_type", False))


def get_base_path():
    return state.get("base_directory")


def get_extensions():
    return state.get("extensions")


def get_post_remove_path():
    return state.get("post_remove_path")


def get_needle():
    return state.get("needle")


def get_replacements():
    return state.get("replace_on_insert")


def build(needle, trigger, current_folder):
    """
        updates state object for given trigger. The state object is then used to query the file cache:

        @see completion
        ProjectManager.search_completions(
                query.get_needle(),
                current_file.get_project_directory(),
                query.get_extensions(),
                query.get_base_path()
            )
    """
    needle = Path.sanitize(needle)
    needle_is_absolute = Path.is_absolute(needle)
    needle_is_path = needle_is_absolute or Path.is_relative(needle)

    if not trigger or not (by_command() or (settings.get("auto_trigger") if needle_is_path else trigger.get("auto", settings.get("auto_trigger")))):
        return False

    """ Adjust current folder by specified base folder:

        BASE-FOLDER ->  CURRENT_FOLDER
        ------------------------------------------------------------
        True            use settings base_directory
        String          use string as base_directory
        False           use current file's directory (parameter)
    """
    base_path = resolve_value("base_directory", trigger, False)
    if base_path is True:
        current_folder = settings.get("base_directory")
    elif base_path:
        current_folder = Path.sanitize_base_directory(base_path)

    state["post_remove_path"] = current_folder if (base_path and needle_is_absolute) else False
    state["base_directory"] = current_folder if resolve_path_type(needle, trigger) == "relative" else False
    state["replace_on_insert"] = resolve_value("replace_on_insert", trigger, [])
    state["extensions"] = resolve_value("extensions", trigger, ["*"])
    state["needle"] = sanitize_needle(needle, current_folder)

    return True


def resolve_path_type(needle, trigger):
    """ ^
        |  Force
        | --------
        |  Needle
        | --------
        | Trigger?
    """
    type_of_path = False # OR RELATIVE BY DEFAULT?
    # test if forced by command
    if override.get("filepath_type"):
        type_of_path = override.get("filepath_type")
    # test path to trigger auto-completion by needle
    elif not by_command() and trigger.get("auto") is False and settings.get("auto_trigger") and Path.is_absolute(needle):
        type_of_path = "absolute"
    elif Path.is_absolute(needle):
        type_of_path = "absolute"
    elif Path.is_relative(needle):
        type_of_path = "relative"
    elif trigger.get("relative") is True:
        type_of_path = "relative"
    elif trigger.get("relative") is False:
        type_of_path = "absolute"

    return type_of_path


def resolve_value(key, trigger, default):
    settings = trigger.get(key, default)
    return override.get(key, settings)


def sanitize_needle(needle, current_folder):
    """
        sanitizes requested path and replaces a starting ./ with the current (local) folder
        returns final needle
    """
    current_folder = "" if not current_folder else current_folder
    needle = re.sub("\.\./", "", needle)
    needle = re.sub("[\\n\\t]", "", needle)

    # remove base path from needle
    if state.get("base_directory") and isinstance(current_folder, str) and needle.startswith(current_folder):
        needle = needle[len(state.get("base_directory")):]

    needle = needle.strip()

    if needle.startswith("./"):
        needle = current_folder + re.sub("\.\/", "", needle)

    # strip any starting dots or slashes
    needle = re.sub("^[\.\/]*", "", needle)

    return needle


================================================
FILE: test/__init__.py
================================================


================================================
FILE: test/integration/__init__.py
================================================


================================================
FILE: test/integration/get_context_test.py
================================================
"""
	get word at cursor
"""
from FuzzyFilePath.test.tools import TestCase
from FuzzyFilePath.expression import get_context


class Test(TestCase):

	#query
	def should_return_line_as_needle(self, viewHelper):
		viewHelper.set_line('../line/as/path')
		viewHelper.move_cursor(0, 12)

		needle = get_context(viewHelper.view).get("needle")

		self.assert_equal(needle, '../line/as/path')

	def should_return_word_as_needle(self, viewHelper):
		viewHelper.set_line('result = path')
		viewHelper.move_cursor(0, 12)

		needle = get_context(viewHelper.view).get("needle")

		self.assert_equal(needle, 'path')

	def should_identify_hbs_partials(self, viewHelper):
		viewHelper.set_line('{{> src/to/folder/file.hbs}}')
		viewHelper.move_cursor(0, 10)

		context = get_context(viewHelper.view)

		self.assert_equal(context.get("needle"), 'src/to/folder/file.hbs')
		self.assert_equal(context.get("region").a, 4)

	#prefix
	def should_return_prefix_before_bracket(self, viewHelper):
		viewHelper.set_line('prefix(file)')
		viewHelper.move_cursor(0, 8)

		result = get_context(viewHelper.view).get("prefix")

		self.assert_equal(result, 'prefix')

	def should_return_prefix_before_equal(self, viewHelper):
		viewHelper.set_line('prefix=file')
		viewHelper.move_cursor(0, 10)

		result = get_context(viewHelper.view).get("prefix")

		self.assert_equal(result, 'prefix')

	def should_return_prefix_before_colon(self, viewHelper):
		viewHelper.set_line('prefix:file')
		viewHelper.move_cursor(0, 10)

		result = get_context(viewHelper.view).get("prefix")

		self.assert_equal(result, 'prefix')

	#style
	def should_return_style_in_quotes(self, vh):
		vh.set_line('"background": prefix(test)')
		vh.move_cursor(0, 23)

		result = get_context(vh.view).get("style")

		self.assert_equal(result, 'background')

	def should_return_style_in_tag(self, vh):
		vh.set_line('<h1 style=\"\'background\': prefix(\'test\')\"')
		vh.move_cursor(0, 36)

		result = get_context(vh.view)

		self.assert_equal(result.get("tagName"), 'h1')
		self.assert_equal(result.get("style"), 'background')

	#tagName
	def should_return_tagName(self, vh):
		vh.set_line('<div id')
		vh.move_cursor(0, 10)

		result = get_context(vh.view).get("tagName")

		self.assert_equal(result, 'div')

	# blacklist
	def should_not_validate_after_closing_bracket(self, vh):
		vh.set_line('require ("require("../package").subPackage');
		vh.move_cursor(0, 35)

		print(get_context(vh.view))
		result = get_context(vh.view).get("valid_needle")

		self.assert_equal(result, False)


================================================
FILE: test/integration/tests.py
================================================
"""
	Loads all integration tests within this folder
	- In order to be loaded each test-file must have "_test" appended
	- Tests are stored and dumped by the file name (without test)
"""
import os
import glob
import importlib


tests = []


modules = glob.glob(os.path.dirname(__file__) + "/*_test.py")
modules = [os.path.basename(f)[:-3] for f in modules]

for f in modules:
	lib = importlib.import_module("FuzzyFilePath.test.integration." + f)
	tests.append(lib.Test(f[:-5]))

================================================
FILE: test/integration/tools.py
================================================
# Integration Test Helpers
import sublime


class ViewHelper:

	window	= None,
	view	= None,
	edit	= None,

	def __init__(self, window, view, edit):
		self.window = window
		self.view = view
		self.edit = edit

	def undo(self, count=1):
		while count > 0:
			self.view.run_command("undo")
			count -= 1

	def set_js_syntax(self):
		self.view.set_syntax_file("Packages/FuzzyFilePath/test/javascript.tmLanguage")

	def set_line(self, string):
		self.view.insert(self.edit, 0, string)

	def move_cursor(self, line, column):
		pt = self.view.text_point(line, column)
		self.view.sel().clear()
		self.view.sel().add(sublime.Region(pt))
		self.window.focus_view(self.view)
		self.view.run_command('_enter_insert_mode', { "mode": "insert" }) # vintageous


================================================
FILE: test/mock/project/app/boot.js
================================================
require("./styles/partials/media.scss")
require("./src/common")
require("../bower_components/angular")


================================================
FILE: test/mock/project/app/index.html
================================================
<!DOCTYPE html>
<html>
	<head>
		<title>FuzzyFilePath project mock</title>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

		<link href="./styles/styles.css"></link>

		<style type="text/css">
			@font-face {
				font-family: "icon";
				src: url(./assets/fonts/glyph.ttf),
					url("./assets/fonts/glyph.svg");
			}
		</style>

		<script src="" type="text/javascript"></script>
		<script type="text/javascript" src="./mock/project/app/boot.js"></script>
		<script type="text/javascript">
			define(["../bower_components/angular/index.js"], function (angular) {
				var main = require("/app/src/main"),
					main_relative = require("./src/main");
			});
		</script>

	</head>
	<body>

		<div id="page" style="background-image: url(./assets/background.png);">
			<img src="../bower_components/lib/assets/logo.jpg" />
		</div>


		<script src="./boot.js"></script>

	</body>
</html>

================================================
FILE: test/mock/project/app/latex.tex
================================================
\input{"./assets/header.png"}

================================================
FILE: test/mock/project/app/src/coffee/coffee.coffee
================================================
require("./module.coffee");
`import "./module.coffee"`



================================================
FILE: test/mock/project/app/src/coffee/module.coffee
================================================


================================================
FILE: test/mock/project/app/src/coffee/other.coffee
================================================


================================================
FILE: test/mock/project/app/src/common.js
================================================


================================================
FILE: test/mock/project/app/src/main.js
================================================
// main
var tools = require("./tools");
var media = require("../styles/partials/media.scss");


================================================
FILE: test/mock/project/app/src/tools.js
================================================


================================================
FILE: test/mock/project/app/src/ts/another.ts
================================================


================================================
FILE: test/mock/project/app/src/ts/module.ts
================================================


================================================
FILE: test/mock/project/app/src/ts/other.ts
================================================


================================================
FILE: test/mock/project/app/src/ts/tsconfig.json
================================================
{
    "compilerOptions": {
        "out": "built/out.js", 
        "sourceMap": true, 
        "target": "es5"
    }, 
    "files": []
}

================================================
FILE: test/mock/project/app/src/ts/typescript.ts
================================================
import * from ./other.ts
import {test} from "../../../bower_components/angular"
from "./another.ts"
import "./typescript";

// <reference path="" />


================================================
FILE: test/mock/project/app/styles/index.css
================================================


.issue13 {
	composes: label from './styles.css'
	composes: label from "sty";

	background-color: rgba(0, 0, 0, 0.2);
}

================================================
FILE: test/mock/project/app/styles/partials/common.scss
================================================

@import ./component.scss

================================================
FILE: test/mock/project/app/styles/partials/media.scss
================================================
// SASS

@import "../../../bower_components/lib/component/component"
@import "/test/mock/project/bower_components/lib/component/component"
@import "./common"

================================================
FILE: test/mock/project/app/styles/styles.css
================================================
* {
	background-image: url("./assets/header.png");
}

@font-face {
	font-family:	"icon";
	src:			url(./assets/fonts/glyph.ttf) format("truetype"),
					url(./assets/fonts/glyph.svg) format("svg");
}

================================================
FILE: test/mock/project/bower_components/angular/index.js
================================================
// angular/index.js

================================================
FILE: test/mock/project/bower_components/lib/component/component.html
================================================
<template>

	<h1></h1>

	<img src="" />

	<p>
		simple text
	</p>

</template>

================================================
FILE: test/mock/project/bower_components/lib/component/component.scss
================================================


================================================
FILE: test/mock/project/bower_components/lib/component/componentCtrl.js
================================================


================================================
FILE: test/mock/project/bower_components/lib/component/componentModel.js
================================================


================================================
FILE: test/mock/project/bower_components/lib/css/index.css
================================================


================================================
FILE: test/mock/project/bower_components/lib/css/styles.css
================================================


================================================
FILE: test/mock/project/bower_components/lib/index.js
================================================


================================================
FILE: test/tools.py
================================================
import inspect

"""
	Inherit from TestCase to run tests receiving argument `ViewHelper`
	Convention: for a test to be executed/recognized its name must contains "should"
"""
class TestCase:

	unit_test = False

	def __init__(self, name):
		self.name = name
		self.tests = self.get_tests()
		self.length = len(self.tests)

	def get_tests(self):
		tests = []
		methods = inspect.getmembers(self, predicate=inspect.ismethod)
		for name, descr in methods:
			if "should" in name:
				tests.append(name)
		return tests

	def assert_equal(self, expect, value):
		assert expect == value, "expected '{0}' to equal '{1}'".format(expect, value)

================================================
FILE: test/unit/__init__.py
================================================


================================================
FILE: test/unit/debug_test.py
================================================
"""
	get word at cursor
"""
from FuzzyFilePath.test.tools import TestCase
import FuzzyFilePath.query as Query

valid_trigger = False

class Test(TestCase):

	def should_set_basepath_to_current_folder(self):
		Query.reset()
		Query.build("", {
			"relative": True,
			"auto": True
		}, "current_folder")

		self.assert_equal(Query.get_base_path(), "current_folder")

	def should_not_set_basepath_for_absolute_queries(self):
		Query.reset()
		Query.build("", {
			"relative": False,
			"auto": True
		}, "current_folder")

		self.assert_equal(Query.get_base_path(), False)


================================================
FILE: test/unit/get_closest_folder_test.py
================================================
from FuzzyFilePath.test.tools import TestCase
from FuzzyFilePath.current_state import get_closest_folder

class FolderMock:
	def __init__(self, directory):
		self.directory = directory


class Test(TestCase):

	def should_return_nearest_folder(self):
		folders = [FolderMock("/Users/Spock"), FolderMock("/Users/Spock/Pictures")]
		result = get_closest_folder("/Users/Spock/Pictures/Uhura", folders)

		self.assert_equal(result.directory, "/Users/Spock/Pictures")

	def should_return_valid_folders_only(self):
		folders = [FolderMock("/Lib"), FolderMock("/Users"), FolderMock("/Users/Spock/Lib")]
		result = get_closest_folder("/Users/Spock", folders)

		self.assert_equal(result.directory, "/Users")

	def should_return_false_if_no_directory_found(self):
		folders = [FolderMock("/Lib"), FolderMock("/Users/Spock"), FolderMock("/Users/Spock/Lib")]
		result = get_closest_folder("/Users/Kirk", folders)

		self.assert_equal(result, False)



================================================
FILE: test/unit/query_test.py
================================================
""" get word at cursor """
from FuzzyFilePath.test.tools import TestCase
import FuzzyFilePath.query as Query


valid_trigger = False

class Test(TestCase):

	def before_each(self):
		global valid_trigger

		Query.reset()
		valid_trigger = {
			"auto": True
		}

	#validation
	def should_abort_on_empty_values(self):
		needle = ""
		current_folder = ""
		trigger = {}

		valid = Query.build(needle, trigger, current_folder)

		self.assert_equal(valid, False)

	def should_be_valid_for_auto(self):
		valid = bool(Query.build("", valid_trigger, ""))

		self.assert_equal(valid, True)

	#base path
	def should_set_basepath_to_current_folder(self):
		valid_trigger["relative"] = True

		Query.build("", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), "current_folder")

	def should_set_base_path_for_relative_queries(self):
		valid_trigger["relative"] = True
		# !Potential problem: path requires a trailing slash (os.path.dirname)
		valid_trigger["base_path"] = "base_path/"

		Query.build("", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), "base_path")

	def should_not_set_basepath_for_absolute_queries(self):
		valid_trigger["relative"] = False

		Query.build("", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), False)

	#basepath override by needle
	def should_prefer_needletype_over_relative_setting_01(self):
		valid_trigger["relative"] = True

		Query.build("/absolute", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), False)

	def should_prefer_needletype_over_relative_setting_02(self):
		valid_trigger["relative"] = False

		Query.build("../relative", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), "current_folder")

	#basepath override by command
	def should_prefer_command_over_rel(self):
		valid_trigger["relative"] = True
		Query.override_trigger_setting("filepath_type", "absolute")

		Query.build("../relative", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), False)

	def should_prefer_command_over_abs(self):
		valid_trigger["relative"] = False
		Query.override_trigger_setting("filepath_type", "relative")

		Query.build("/absolute", valid_trigger, "current_folder")

		self.assert_equal(Query.get_base_path(), "current_folder")

	#swap rel <-> abs
	def should_transform_rel_to_abs_query(self):
		Query.override_trigger_setting("filepath_type", "absolute") # set query to be absolute
		Query.build("../folder/sub", valid_trigger, "current_folder") # but insert relative path

		self.assert_equal(Query.get_needle(), "folder/sub")
		self.assert_equal(Query.get_base_path(), False)

	def should_transform_abs_to_rel_query(self):
		Query.override_trigger_setting("filepath_type", "relative") # set query to be relative
		Query.build("/folder/sub", valid_trigger, "current_folder") # but insert absolute path

		self.assert_equal(Query.get_needle(), "folder/sub")
		self.assert_equal(Query.get_base_path(), "current_folder")




================================================
FILE: test/unit/tests.py
================================================
"""
	Loads all integration tests within this folder
	- In order to be loaded each test-file must have "_test" appended
	- Tests are stored and dumped by the file name (without test)
"""
import os
import glob
import importlib


tests = []


modules = glob.glob(os.path.dirname(__file__) + "/*_test.py")
modules = [os.path.basename(f)[:-3] for f in modules]

for f in modules:
	lib = importlib.import_module("FuzzyFilePath.test.unit." + f)
	tests.append(lib.Test(f[:-5]))
Download .txt
gitextract_1a51ib0w/

├── .gitignore
├── .npmignore
├── CHANGES.md
├── Default (Linux).sublime-keymap
├── Default (OSX).sublime-keymap
├── Default (Windows).sublime-keymap
├── Default.sublime-commands
├── Default.sublime-keymap
├── FuzzyFilePath.md
├── FuzzyFilePath.sublime-settings
├── LICENSE
├── Main.sublime-menu
├── ProjectListener.py
├── QueryCompletionListener.py
├── README.md
├── TestRunner.py
├── ViewListener.py
├── command_goto_file.py
├── command_insert_path.py
├── command_rebuild_cache.py
├── command_replace_region.py
├── command_show_context.py
├── command_show_current_settings.py
├── command_show_info.py
├── common/
│   ├── __init__.py
│   ├── config.py
│   ├── path.py
│   ├── selection.py
│   ├── settings.py
│   ├── string.py
│   └── verbose.py
├── completion.py
├── controller.py
├── current_state.py
├── expression.py
├── project/
│   ├── FileCache.py
│   ├── FileCacheWorker.py
│   ├── __init__.py
│   └── validate.py
├── query.py
└── test/
    ├── __init__.py
    ├── integration/
    │   ├── __init__.py
    │   ├── get_context_test.py
    │   ├── tests.py
    │   └── tools.py
    ├── mock/
    │   └── project/
    │       ├── app/
    │       │   ├── boot.js
    │       │   ├── index.html
    │       │   ├── latex.tex
    │       │   ├── src/
    │       │   │   ├── coffee/
    │       │   │   │   ├── coffee.coffee
    │       │   │   │   ├── module.coffee
    │       │   │   │   └── other.coffee
    │       │   │   ├── common.js
    │       │   │   ├── main.js
    │       │   │   ├── tools.js
    │       │   │   └── ts/
    │       │   │       ├── another.ts
    │       │   │       ├── module.ts
    │       │   │       ├── other.ts
    │       │   │       ├── tsconfig.json
    │       │   │       └── typescript.ts
    │       │   └── styles/
    │       │       ├── index.css
    │       │       ├── partials/
    │       │       │   ├── common.scss
    │       │       │   └── media.scss
    │       │       └── styles.css
    │       └── bower_components/
    │           ├── angular/
    │           │   └── index.js
    │           └── lib/
    │               ├── component/
    │               │   ├── component.html
    │               │   ├── component.scss
    │               │   ├── componentCtrl.js
    │               │   └── componentModel.js
    │               ├── css/
    │               │   ├── index.css
    │               │   └── styles.css
    │               └── index.js
    ├── tools.py
    └── unit/
        ├── __init__.py
        ├── debug_test.py
        ├── get_closest_folder_test.py
        ├── query_test.py
        └── tests.py
Download .txt
SYMBOL INDEX (194 symbols across 30 files)

FILE: ProjectListener.py
  class ProjectListener (line 10) | class ProjectListener(sublime_plugin.EventListener):
    method on_activated (line 16) | def on_activated(self, view):
    method on_window_activated (line 33) | def on_window_activated(self, view):
    method on_project_activated (line 37) | def on_project_activated(self, view):
  function get_project_id (line 44) | def get_project_id(window):

FILE: QueryCompletionListener.py
  class QueryCompletionListener (line 14) | class QueryCompletionListener(sublime_plugin.EventListener):
    method on_query_completions (line 23) | def on_query_completions(self, view, prefix, locations):
    method on_post_insert_completion (line 38) | def on_post_insert_completion(self, view, command_name):
    method on_query_abort (line 42) | def on_query_abort(self):
    method start_tracking (line 46) | def start_tracking(self, view, command_name=None):
    method finish_tracking (line 64) | def finish_tracking(self, view, command_name=None):
    method abort_tracking (line 68) | def abort_tracking(self):
    method on_text_command (line 72) | def on_text_command(self, view, command_name, args):
    method on_post_text_command (line 81) | def on_post_text_command(self, view, command_name, args):

FILE: TestRunner.py
  class FfpTestRunner (line 21) | class FfpTestRunner(sublime_plugin.TextCommand):
    method console_blur (line 25) | def console_blur(self):
    method console_open (line 30) | def console_open(self):
    method after (line 35) | def after(self):
    method setUp (line 43) | def setUp(self, edit):
    method closeView (line 48) | def closeView(self): {
    method tearDown (line 52) | def tearDown(self, failed_tests, total_tests):
    method run (line 64) | def run(self, edit):

FILE: ViewListener.py
  function is_valid (line 12) | def is_valid(view):
  class ViewListener (line 20) | class ViewListener(sublime_plugin.EventListener):
    method on_post_save_async (line 21) | def on_post_save_async(self, view):
    method on_activated (line 26) | def on_activated(self, view):
    method on_file_created (line 31) | def on_file_created(self, view):

FILE: command_goto_file.py
  class FfpGotoFileCommand (line 15) | class FfpGotoFileCommand(sublime_plugin.TextCommand):
    method run (line 17) | def run(self, edit):
    method filepath_exists (line 62) | def filepath_exists(self, filepath):
    method select_file (line 65) | def select_file(self, index):
    method open_file (line 70) | def open_file(self, project_folder, filepath):

FILE: command_insert_path.py
  class InsertPathCommand (line 6) | class InsertPathCommand(sublime_plugin.TextCommand):
    method run (line 8) | def run(self, edit, type="default", base_directory=None, replace_on_in...

FILE: command_rebuild_cache.py
  class FfpUpdateCacheCommand (line 6) | class FfpUpdateCacheCommand(sublime_plugin.TextCommand):
    method run (line 8) | def run(self, edit):

FILE: command_replace_region.py
  class FfpReplaceRegionCommand (line 7) | class FfpReplaceRegionCommand(sublime_plugin.TextCommand):
    method run (line 10) | def run(self, edit, a, b, string, move_cursor=False):
    method move_skip (line 19) | def move_skip(self, point):
    method move_cursor (line 32) | def move_cursor(self, point):

FILE: command_show_context.py
  class FfpShowContextCommand (line 9) | class FfpShowContextCommand(sublime_plugin.TextCommand):
    method run (line 14) | def run(self, edit):
    method add (line 41) | def add(self, label, value):
    method show (line 45) | def show(self):

FILE: command_show_current_settings.py
  class FfpShowCurrentSettingsCommand (line 7) | class FfpShowCurrentSettingsCommand(sublime_plugin.TextCommand):
    method run (line 9) | def run(self, edit):

FILE: command_show_info.py
  class FfpShowInfoCommand (line 6) | class FfpShowInfoCommand(sublime_plugin.TextCommand):
    method run (line 8) | def run(self, edit):

FILE: common/path.py
  function sanitize (line 4) | def sanitize(path):
  function posix (line 12) | def posix(path):
  function is_relative (line 17) | def is_relative(string):
  function is_absolute (line 20) | def is_absolute(string):
  function sanitize_base_directory (line 23) | def sanitize_base_directory(path):
  function get_absolute_path (line 31) | def get_absolute_path(base_path, relative_path):
  function get_relative_folder (line 38) | def get_relative_folder(file_name, base_directory):
  function relative_to (line 44) | def relative_to(base_directory, folder_path):
  function trace (line 51) | def trace(from_folder, to_folder):

FILE: common/selection.py
  function get_position (line 2) | def get_position(view):
  function get_line (line 6) | def get_line(view):
  function get_word (line 11) | def get_word(view):
  function get_scope (line 15) | def get_scope(view):

FILE: common/settings.py
  function get (line 26) | def get(key, default=None):
  function current (line 30) | def current():
  function update (line 34) | def update():
  function update_base_settings (line 40) | def update_base_settings():
  function update_project_settings (line 49) | def update_project_settings():
  function update_project_folder_settings (line 56) | def update_project_folder_settings(project_folder):
  function get_project_settings (line 62) | def get_project_settings(base):
  function get_base_settings (line 72) | def get_base_settings(config):
  function get_folder_settings (line 80) | def get_folder_settings(project, project_folder=None):
  function merge (line 89) | def merge(settings, overwrite={}):
  function merge_scopes (line 102) | def merge_scopes(settings):
  function get_folder_setting (line 113) | def get_folder_setting(folder=None):
  function sanitize (line 140) | def sanitize(settings_object):
  function verbose (line 148) | def verbose(*args):

FILE: common/string.py
  function get_diff (line 1) | def get_diff(first, second):
  function get_start_diff (line 15) | def get_start_diff(first, second):
  function get_end_diff (line 27) | def get_end_diff(first, second):

FILE: common/verbose.py
  function log (line 5) | def log(*args):
  function verbose (line 9) | def verbose(*args):
  function warn (line 13) | def warn(*args):
  function start_block (line 16) | def start_block():
  function end_block (line 20) | def end_block():

FILE: completion.py
  function start (line 29) | def start(post_replacements=[]):
  function stop (line 34) | def stop():
  function is_active (line 39) | def is_active():
  function resolve_trigger (line 43) | def resolve_trigger(view, query):
  function get_filepaths (line 63) | def get_filepaths(view, query):
  function find_trigger (line 84) | def find_trigger(current_scope, expression, byCommand=False):
  function update_inserted_filepath (line 97) | def update_inserted_filepath(view, post_remove):
  function get_matching_autotriggers (line 121) | def get_matching_autotriggers(scope, triggers):
  function apply_post_replacements (line 135) | def apply_post_replacements(path, base_directory, replace_on_insert):

FILE: controller.py
  function plugin_loaded (line 14) | def plugin_loaded():
  function update_settings (line 21) | def update_settings():
  function get_filepath_completions (line 32) | def get_filepath_completions(view):
  function on_query_completion_inserted (line 53) | def on_query_completion_inserted(view, post_remove):
  function on_query_completion_aborted (line 60) | def on_query_completion_aborted():
  function on_project_focus (line 65) | def on_project_focus(window):
  function on_project_activated (line 72) | def on_project_activated(window):
  function on_file_created (line 80) | def on_file_created():
  function on_file_focus (line 87) | def on_file_focus(view):

FILE: current_state.py
  function update (line 23) | def update():
  function sanitize_directory (line 80) | def sanitize_directory(file_name, project_folder):
  function get_project_directory (line 86) | def get_project_directory():
  function get_directory (line 90) | def get_directory():
  function update_settings (line 94) | def update_settings():
  function is_valid (line 100) | def is_valid():
  function enable (line 104) | def enable():
  function get_file_cache (line 109) | def get_file_cache(folder):
  function rebuild_filecache (line 118) | def rebuild_filecache(folder=None):
  function search_completions (line 134) | def search_completions(needle, project_folder, valid_extensions, base_pa...
  function find_file (line 138) | def find_file(file_name):
  function get_valid_extensions (line 142) | def get_valid_extensions(triggers):
  function get_closest_folder (line 152) | def get_closest_folder(filepath, directories):

FILE: expression.py
  function get_context (line 16) | def get_context(view):
  function check_trigger (line 123) | def check_trigger(trigger, expression):
  function find_trigger (line 136) | def find_trigger(expression, scope, triggers):
  function get_rule (line 147) | def get_rule(view):

FILE: project/FileCache.py
  class FileCache (line 14) | class FileCache:
    method __init__ (line 20) | def __init__(self, file_extensions, exclude_folders, directory):
    method update_settings (line 28) | def update_settings(self, file_extensions, exclude_folders):
    method search_completions (line 35) | def search_completions(self, needle, project_folder, valid_extensions,...
    method find_file (line 100) | def find_file(self, file_name):
    method get_completion (line 112) | def get_completion(self, target_path, base_path=False):
    method file_is_cached (line 120) | def file_is_cached(self, file_name):
    method rebuild (line 134) | def rebuild(self):

FILE: project/FileCacheWorker.py
  function posix (line 20) | def posix(path):
  class FileCacheWorker (line 24) | class FileCacheWorker(threading.Thread):
    method __init__ (line 26) | def __init__(self, exclude_folders, extensions, folder):
    method run (line 34) | def run(self):
    method read (line 41) | def read(self, folder, base=None):

FILE: project/validate.py
  function view (line 16) | def view(view, config, open_dialog=False):
  function get_current_folder_relative (line 53) | def get_current_folder_relative(view, project_directory):
  function file_has_location (line 57) | def file_has_location(view):
  function is_project (line 61) | def is_project():
  function get_valid_path (line 65) | def get_valid_path(string):
  function project_directory (line 69) | def project_directory(view, project_directory):
  function sanitize_base_directory (line 103) | def sanitize_base_directory(base_directory, project_directory, base_proj...
  function show_dialog (line 141) | def show_dialog(message, open=False):

FILE: query.py
  function reset (line 25) | def reset():
  function override_trigger_setting (line 32) | def override_trigger_setting(key, value):
  function by_command (line 36) | def by_command():
  function get_base_path (line 40) | def get_base_path():
  function get_extensions (line 44) | def get_extensions():
  function get_post_remove_path (line 48) | def get_post_remove_path():
  function get_needle (line 52) | def get_needle():
  function get_replacements (line 56) | def get_replacements():
  function build (line 60) | def build(needle, trigger, current_folder):
  function resolve_path_type (line 102) | def resolve_path_type(needle, trigger):
  function resolve_value (line 129) | def resolve_value(key, trigger, default):
  function sanitize_needle (line 134) | def sanitize_needle(needle, current_folder):

FILE: test/integration/get_context_test.py
  class Test (line 8) | class Test(TestCase):
    method should_return_line_as_needle (line 11) | def should_return_line_as_needle(self, viewHelper):
    method should_return_word_as_needle (line 19) | def should_return_word_as_needle(self, viewHelper):
    method should_identify_hbs_partials (line 27) | def should_identify_hbs_partials(self, viewHelper):
    method should_return_prefix_before_bracket (line 37) | def should_return_prefix_before_bracket(self, viewHelper):
    method should_return_prefix_before_equal (line 45) | def should_return_prefix_before_equal(self, viewHelper):
    method should_return_prefix_before_colon (line 53) | def should_return_prefix_before_colon(self, viewHelper):
    method should_return_style_in_quotes (line 62) | def should_return_style_in_quotes(self, vh):
    method should_return_style_in_tag (line 70) | def should_return_style_in_tag(self, vh):
    method should_return_tagName (line 80) | def should_return_tagName(self, vh):
    method should_not_validate_after_closing_bracket (line 89) | def should_not_validate_after_closing_bracket(self, vh):

FILE: test/integration/tools.py
  class ViewHelper (line 5) | class ViewHelper:
    method __init__ (line 11) | def __init__(self, window, view, edit):
    method undo (line 16) | def undo(self, count=1):
    method set_js_syntax (line 21) | def set_js_syntax(self):
    method set_line (line 24) | def set_line(self, string):
    method move_cursor (line 27) | def move_cursor(self, line, column):

FILE: test/tools.py
  class TestCase (line 7) | class TestCase:
    method __init__ (line 11) | def __init__(self, name):
    method get_tests (line 16) | def get_tests(self):
    method assert_equal (line 24) | def assert_equal(self, expect, value):

FILE: test/unit/debug_test.py
  class Test (line 9) | class Test(TestCase):
    method should_set_basepath_to_current_folder (line 11) | def should_set_basepath_to_current_folder(self):
    method should_not_set_basepath_for_absolute_queries (line 20) | def should_not_set_basepath_for_absolute_queries(self):

FILE: test/unit/get_closest_folder_test.py
  class FolderMock (line 4) | class FolderMock:
    method __init__ (line 5) | def __init__(self, directory):
  class Test (line 9) | class Test(TestCase):
    method should_return_nearest_folder (line 11) | def should_return_nearest_folder(self):
    method should_return_valid_folders_only (line 17) | def should_return_valid_folders_only(self):
    method should_return_false_if_no_directory_found (line 23) | def should_return_false_if_no_directory_found(self):

FILE: test/unit/query_test.py
  class Test (line 8) | class Test(TestCase):
    method before_each (line 10) | def before_each(self):
    method should_abort_on_empty_values (line 19) | def should_abort_on_empty_values(self):
    method should_be_valid_for_auto (line 28) | def should_be_valid_for_auto(self):
    method should_set_basepath_to_current_folder (line 34) | def should_set_basepath_to_current_folder(self):
    method should_set_base_path_for_relative_queries (line 41) | def should_set_base_path_for_relative_queries(self):
    method should_not_set_basepath_for_absolute_queries (line 50) | def should_not_set_basepath_for_absolute_queries(self):
    method should_prefer_needletype_over_relative_setting_01 (line 58) | def should_prefer_needletype_over_relative_setting_01(self):
    method should_prefer_needletype_over_relative_setting_02 (line 65) | def should_prefer_needletype_over_relative_setting_02(self):
    method should_prefer_command_over_rel (line 73) | def should_prefer_command_over_rel(self):
    method should_prefer_command_over_abs (line 81) | def should_prefer_command_over_abs(self):
    method should_transform_rel_to_abs_query (line 90) | def should_transform_rel_to_abs_query(self):
    method should_transform_abs_to_rel_query (line 97) | def should_transform_abs_to_rel_query(self):
Condensed preview — 77 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (104K chars).
[
  {
    "path": ".gitignore",
    "chars": 80,
    "preview": "/temp\n*.pyc\n*.psd\nFuzzyFilePath.sublime-workspace\nFuzzyFilePath.sublime-project\n"
  },
  {
    "path": ".npmignore",
    "chars": 11,
    "preview": "/test\n*.gif"
  },
  {
    "path": "CHANGES.md",
    "chars": 2924,
    "preview": "19/04/03\n\n- Update triggers for sass, js and ts\n\n18/05/06\n\n- Add `additional_scopes` list for custom scope extension\n- U"
  },
  {
    "path": "Default (Linux).sublime-keymap",
    "chars": 369,
    "preview": "[\n    {\n        \"keys\": [\"ctrl+alt+space\"],\n        \"command\": \"insert_path\",\n        \"args\": {\n            \"type\": \"rel"
  },
  {
    "path": "Default (OSX).sublime-keymap",
    "chars": 369,
    "preview": "[\n    {\n        \"keys\": [\"ctrl+alt+space\"],\n        \"command\": \"insert_path\",\n        \"args\": {\n            \"type\": \"rel"
  },
  {
    "path": "Default (Windows).sublime-keymap",
    "chars": 369,
    "preview": "[\n    {\n        \"keys\": [\"ctrl+alt+space\"],\n        \"command\": \"insert_path\",\n        \"args\": {\n            \"type\": \"rel"
  },
  {
    "path": "Default.sublime-commands",
    "chars": 687,
    "preview": "[\n\t{ \"caption\": \"FuzzyFilePath: Rebuild cache\", \"command\": \"ffp_update_cache\" },\n\t{ \"caption\": \"FuzzyFilePath: Show info"
  },
  {
    "path": "Default.sublime-keymap",
    "chars": 369,
    "preview": "[\n    {\n        \"keys\": [\"ctrl+alt+space\"],\n        \"command\": \"insert_path\",\n        \"args\": {\n            \"type\": \"rel"
  },
  {
    "path": "FuzzyFilePath.md",
    "chars": 1593,
    "preview": "# FuzzyFilePath - autocomplete filepaths\n\n@version 0.6.1\n@author Sascha Goldhofer <post@saschagoldhofer.de>\n\n## tasks\n\n-"
  },
  {
    "path": "FuzzyFilePath.sublime-settings",
    "chars": 6632,
    "preview": "{\n\t// set project_directory relative to sublime project directory\n\t\"project_directory\": \"\",\n\t// base directory for paths"
  },
  {
    "path": "LICENSE",
    "chars": 565,
    "preview": "\n                     DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n                             Version 2, December 2004\n"
  },
  {
    "path": "Main.sublime-menu",
    "chars": 1443,
    "preview": "[\n    {\n        \"caption\": \"Preferences\",\n        \"mnemonic\": \"n\",\n        \"id\": \"preferences\",\n        \"children\":\n    "
  },
  {
    "path": "ProjectListener.py",
    "chars": 1324,
    "preview": "import sublime\nimport sublime_plugin\n\nimport FuzzyFilePath.controller as controller\n\n\nID = \"ProjectListener\"\n\n\nclass Pro"
  },
  {
    "path": "QueryCompletionListener.py",
    "chars": 3267,
    "preview": "import re\nimport sublime_plugin\nimport FuzzyFilePath.controller as controller\nimport FuzzyFilePath.common.selection as S"
  },
  {
    "path": "README.md",
    "chars": 4188,
    "preview": "# [FuzzyFilePath](https://github.com/sagold/FuzzyFilePath)\n\n__Sublime Text Plugin__\n\nFuzzy search and insert filenames i"
  },
  {
    "path": "TestRunner.py",
    "chars": 2961,
    "preview": "\"\"\"\n\tFuzzyFilePath unit and integration test runner\n\n\tUsage:\n\t\tBind a shortcut like\n\t\t{ \"keys\": [\"super+y\"], \"command\": "
  },
  {
    "path": "ViewListener.py",
    "chars": 762,
    "preview": "import re\nimport copy\nimport sublime_plugin\n\nimport FuzzyFilePath.controller as controller\nimport FuzzyFilePath.current_"
  },
  {
    "path": "command_goto_file.py",
    "chars": 2678,
    "preview": "import os\nimport re\nimport sublime\nimport sublime_plugin\nimport FuzzyFilePath.expression as Context\nimport FuzzyFilePath"
  },
  {
    "path": "command_insert_path.py",
    "chars": 832,
    "preview": "import sublime_plugin\nimport FuzzyFilePath.common.settings as settings\nimport FuzzyFilePath.query as Query\n\n\nclass Inser"
  },
  {
    "path": "command_rebuild_cache.py",
    "chars": 305,
    "preview": "import sublime\nimport sublime_plugin\nimport FuzzyFilePath.current_state as state\n\n\nclass FfpUpdateCacheCommand(sublime_p"
  },
  {
    "path": "command_replace_region.py",
    "chars": 1145,
    "preview": "import re\nimport sublime\nimport sublime_plugin\nimport FuzzyFilePath.common.settings as settings\n\n\nclass FfpReplaceRegion"
  },
  {
    "path": "command_show_context.py",
    "chars": 1496,
    "preview": "import sublime_plugin\n\nimport FuzzyFilePath.expression as Context\nimport FuzzyFilePath.common.selection as Selection\nfro"
  },
  {
    "path": "command_show_current_settings.py",
    "chars": 529,
    "preview": "import json\nimport sublime\nimport sublime_plugin\nimport FuzzyFilePath.common.settings as settings\n\n\nclass FfpShowCurrent"
  },
  {
    "path": "command_show_info.py",
    "chars": 343,
    "preview": "import sublime_plugin\nimport FuzzyFilePath.common.settings as settings\nimport FuzzyFilePath.project.validate as Validate"
  },
  {
    "path": "common/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "common/config.py",
    "chars": 660,
    "preview": "config = {\n\n\t\"debug\": False,\n\t\"log\": False,\n\t\"id\": \"config\",\n\t\"ffp_settings_file\": \"FuzzyFilePath.sublime-settings\",\n\t\"e"
  },
  {
    "path": "common/path.py",
    "chars": 2203,
    "preview": "import re\nimport os\n\ndef sanitize(path):\n    # sanitize slashes (posix)\n    path = posix(path)\n    # sanitize ././\n    p"
  },
  {
    "path": "common/selection.py",
    "chars": 402,
    "preview": "\ndef get_position(view):\n\tselection = view.sel()\n\treturn selection[0].begin() if selection else \"\"\n\ndef get_line(view):\n"
  },
  {
    "path": "common/settings.py",
    "chars": 4860,
    "preview": "\"\"\"\n    manages the current setting data which is retrieved in the following order (bottom to top)\n    - project setting"
  },
  {
    "path": "common/string.py",
    "chars": 887,
    "preview": "def get_diff(first, second):\n    # get intersection at start\n    start = get_start_diff(first, second)\n\n    # remove int"
  },
  {
    "path": "common/verbose.py",
    "chars": 520,
    "preview": "import FuzzyFilePath.common.settings as settings\n\nIGNORE = [\"CurrentFile\", \"QueryCompletionListener\", \"search\", \"Express"
  },
  {
    "path": "completion.py",
    "chars": 4851,
    "preview": "\"\"\"\n    Manage active state of current completion and post cleanup\n\"\"\"\nimport re\nimport sublime\nimport FuzzyFilePath.exp"
  },
  {
    "path": "controller.py",
    "chars": 2285,
    "preview": "import sublime\nimport FuzzyFilePath.completion as Completion\nimport FuzzyFilePath.query as Query\nimport FuzzyFilePath.co"
  },
  {
    "path": "current_state.py",
    "chars": 4994,
    "preview": "\"\"\"\n\tmanages current state, which include: current filename of view, current project folder if any, project folder\n\tdoes"
  },
  {
    "path": "expression.py",
    "chars": 4734,
    "preview": "import re\nimport sublime\nimport FuzzyFilePath.common.settings as settings\nimport FuzzyFilePath.common.selection as Selec"
  },
  {
    "path": "project/FileCache.py",
    "chars": 5511,
    "preview": "import sublime\nimport os\nimport gc\nimport re\nfrom FuzzyFilePath.common.verbose import verbose\nfrom FuzzyFilePath.common."
  },
  {
    "path": "project/FileCacheWorker.py",
    "chars": 3251,
    "preview": "\"\"\"\n    Scans, parses and stores all files in the given folder to the dictionary `files`\n\n    Each file entry is set by "
  },
  {
    "path": "project/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "project/validate.py",
    "chars": 5745,
    "preview": "\"\"\"\n    Validates the current file`s folders based on project folders and its settings.\n    Reminder: project = sublime "
  },
  {
    "path": "query.py",
    "chars": 4628,
    "preview": "\"\"\"\n    Build current query based on received modifiers\n\"\"\"\nimport re\nimport FuzzyFilePath.common.path as Path\nfrom Fuzz"
  },
  {
    "path": "test/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/integration/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/integration/get_context_test.py",
    "chars": 2519,
    "preview": "\"\"\"\n\tget word at cursor\n\"\"\"\nfrom FuzzyFilePath.test.tools import TestCase\nfrom FuzzyFilePath.expression import get_conte"
  },
  {
    "path": "test/integration/tests.py",
    "chars": 476,
    "preview": "\"\"\"\n\tLoads all integration tests within this folder\n\t- In order to be loaded each test-file must have \"_test\" appended\n\t"
  },
  {
    "path": "test/integration/tools.py",
    "chars": 748,
    "preview": "# Integration Test Helpers\nimport sublime\n\n\nclass ViewHelper:\n\n\twindow\t= None,\n\tview\t= None,\n\tedit\t= None,\n\n\tdef __init_"
  },
  {
    "path": "test/mock/project/app/boot.js",
    "chars": 103,
    "preview": "require(\"./styles/partials/media.scss\")\nrequire(\"./src/common\")\nrequire(\"../bower_components/angular\")\n"
  },
  {
    "path": "test/mock/project/app/index.html",
    "chars": 905,
    "preview": "<!DOCTYPE html>\n<html>\n\t<head>\n\t\t<title>FuzzyFilePath project mock</title>\n\t\t<meta http-equiv=\"Content-Type\" content=\"te"
  },
  {
    "path": "test/mock/project/app/latex.tex",
    "chars": 29,
    "preview": "\\input{\"./assets/header.png\"}"
  },
  {
    "path": "test/mock/project/app/src/coffee/coffee.coffee",
    "chars": 56,
    "preview": "require(\"./module.coffee\");\n`import \"./module.coffee\"`\n\n"
  },
  {
    "path": "test/mock/project/app/src/coffee/module.coffee",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/coffee/other.coffee",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/common.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/main.js",
    "chars": 94,
    "preview": "// main\nvar tools = require(\"./tools\");\nvar media = require(\"../styles/partials/media.scss\");\n"
  },
  {
    "path": "test/mock/project/app/src/tools.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/ts/another.ts",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/ts/module.ts",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/ts/other.ts",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/app/src/ts/tsconfig.json",
    "chars": 136,
    "preview": "{\n    \"compilerOptions\": {\n        \"out\": \"built/out.js\", \n        \"sourceMap\": true, \n        \"target\": \"es5\"\n    }, \n "
  },
  {
    "path": "test/mock/project/app/src/ts/typescript.ts",
    "chars": 149,
    "preview": "import * from ./other.ts\nimport {test} from \"../../../bower_components/angular\"\nfrom \"./another.ts\"\nimport \"./typescript"
  },
  {
    "path": "test/mock/project/app/styles/index.css",
    "chars": 120,
    "preview": "\n\n.issue13 {\n\tcomposes: label from './styles.css'\n\tcomposes: label from \"sty\";\n\n\tbackground-color: rgba(0, 0, 0, 0.2);\n}"
  },
  {
    "path": "test/mock/project/app/styles/partials/common.scss",
    "chars": 25,
    "preview": "\n@import ./component.scss"
  },
  {
    "path": "test/mock/project/app/styles/partials/media.scss",
    "chars": 157,
    "preview": "// SASS\n\n@import \"../../../bower_components/lib/component/component\"\n@import \"/test/mock/project/bower_components/lib/co"
  },
  {
    "path": "test/mock/project/app/styles/styles.css",
    "chars": 198,
    "preview": "* {\n\tbackground-image: url(\"./assets/header.png\");\n}\n\n@font-face {\n\tfont-family:\t\"icon\";\n\tsrc:\t\t\turl(./assets/fonts/glyp"
  },
  {
    "path": "test/mock/project/bower_components/angular/index.js",
    "chars": 19,
    "preview": "// angular/index.js"
  },
  {
    "path": "test/mock/project/bower_components/lib/component/component.html",
    "chars": 78,
    "preview": "<template>\n\n\t<h1></h1>\n\n\t<img src=\"\" />\n\n\t<p>\n\t\tsimple text\n\t</p>\n\n</template>"
  },
  {
    "path": "test/mock/project/bower_components/lib/component/component.scss",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/bower_components/lib/component/componentCtrl.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/bower_components/lib/component/componentModel.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/bower_components/lib/css/index.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/bower_components/lib/css/styles.css",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/mock/project/bower_components/lib/index.js",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/tools.py",
    "chars": 634,
    "preview": "import inspect\n\n\"\"\"\n\tInherit from TestCase to run tests receiving argument `ViewHelper`\n\tConvention: for a test to be ex"
  },
  {
    "path": "test/unit/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "test/unit/debug_test.py",
    "chars": 571,
    "preview": "\"\"\"\n\tget word at cursor\n\"\"\"\nfrom FuzzyFilePath.test.tools import TestCase\nimport FuzzyFilePath.query as Query\n\nvalid_tri"
  },
  {
    "path": "test/unit/get_closest_folder_test.py",
    "chars": 939,
    "preview": "from FuzzyFilePath.test.tools import TestCase\nfrom FuzzyFilePath.current_state import get_closest_folder\n\nclass FolderMo"
  },
  {
    "path": "test/unit/query_test.py",
    "chars": 3015,
    "preview": "\"\"\" get word at cursor \"\"\"\nfrom FuzzyFilePath.test.tools import TestCase\nimport FuzzyFilePath.query as Query\n\n\nvalid_tri"
  },
  {
    "path": "test/unit/tests.py",
    "chars": 470,
    "preview": "\"\"\"\n\tLoads all integration tests within this folder\n\t- In order to be loaded each test-file must have \"_test\" appended\n\t"
  }
]

About this extraction

This page contains the full source code of the sagold/FuzzyFilePath GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 77 files (90.1 KB), approximately 23.4k tokens, and a symbol index with 194 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!