Skip to content

Commit

Permalink
Merge pull request #16 from bubenkoff/add-gherkin-auto-complete
Browse files Browse the repository at this point in the history
Added gherkin auto-complete plugin
  • Loading branch information
bubenkoff committed Jan 27, 2015
2 parents 46fb2ae + 7544400 commit 33b951e
Show file tree
Hide file tree
Showing 3 changed files with 202 additions and 1 deletion.
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Changelog
---------

Unreleased
----------

* Added gherkin auto-complete plugin (bubenkoff)
178 changes: 178 additions & 0 deletions GherkinAutoComplete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
"""
Gherkin Auto-Complete Sublime Text Plugin.
Copyright 2013, Andy Hitchman
Author: Andy Hitchman
Version: 0.1
License: MIT
Description: Show all existing gherkin phrases within features in folder hierarchy
Based on the work of Elad Yarkoni. Thank you!
See http://www.eladyarkoni.com/2012/09/sublime-text-auto-complete-plugin.html
"""
import sublime
import sublime_plugin
import codecs
import os
import re


class Phrase:

"""Gherkin phrase (step)."""

def __init__(self, phrase, predicate, feature_name, file_name):
self.phrase = phrase
self.predicate = predicate
self.feature_name = feature_name
self.file_name = file_name


class GherkinPhrases:

"""Phrase collection."""

phrases = []

def clearPhrases(self):
self.phrases = []

def clearPhrasesForFeatureFile(self, file_name):
self.phrases = [
phrase for phrase in self.phrases if phrase.file_name != file_name]

def addPhrase(self, predicate, phrase, feature_name, file_name):
self.phrases.append(Phrase(phrase, predicate, feature_name, file_name))
self.phrases.sort(key=lambda phrase: phrase.phrase)

def get_match_on(self, activePredicate):
continuations = '|and|but'
return '(?P<given>given' + (continuations if activePredicate == 'given' else '') + ')|\
(?P<when>when' + (continuations if activePredicate == 'when' else '') + ')|\
(?P<then>then' + (continuations if activePredicate == 'then' else '') + ')'

def get_autocomplete_list(self, word, predicate=None):
word = word.strip()
autocomplete_list = []
phrases = set()
for phrase in self.phrases:
if (
(word in phrase.phrase or not word)
and (predicate == phrase.predicate if predicate else True)
and phrase.phrase not in phrases
):
autocomplete_list.append((
phrase.phrase + '\t' + phrase.predicate + ' in ' + phrase.feature_name, phrase.phrase))
phrases.add(phrase.phrase)
return autocomplete_list

def is_feature_file(self, filename):
return '.feature' in filename


class GherkinAutoComplete(GherkinPhrases, sublime_plugin.EventListener):

"""Gherkin autocomplete plugin."""

all_indexed = False

def on_activated_async(self, view):
if not self.all_indexed:
self.all_indexed = True
self.index_all_features(view.window().folders())

def on_post_save_async(self, view):
if self.is_feature_file(view.file_name()):
if self.all_indexed:
self.clearPhrasesForFeatureFile(view.file_name())
self.index_file(view.file_name())
else:
self.all_indexed = True
self.index_all_features(view.window().folders())

def index_all_features(self, open_folders):
self.clearPhrases()

for folder in open_folders:
feature_files = self.get_feature_files(folder)
for file_name in feature_files:
self.index_file(file_name)

def on_query_completions(self, view, prefix, locations):
completions = []

if self.is_feature_file(view.file_name()):
predicate = None
for sel in view.sel():
if sel.empty():
match = None
while not match or sel.b > -1:
line = view.substr(view.line(sel.b)).strip()
match = re.match(r'^(given|when|then)', line, re.IGNORECASE)
if match:
predicate = match.group(0).lower()
break
else:
row, col = view.rowcol(sel.b)
sel.b = view.text_point(row - 1, col)
break
completions = self.get_autocomplete_list(
prefix, predicate=predicate)

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

def index_file(self, file_name):
print('Indexing gherkin phrases in ' + file_name)
sublime.status_message('Indexing gherkin phrases in ' + file_name)
self.index_phrases(
file_name, codecs.open(file_name, 'rU', encoding='utf-8'))

def index_phrases(self, file_name, lines):
feature_name = file_name
activePredicate = None
collecting_table_data = False
phrase = None

for line in lines:
match = re.match(r'^\s*feature:\s*(.*)$', line, re.IGNORECASE)
if match is not None:
feature_name = match.group(1)

# Collect table data or emit phrase as soon as we have no match
if activePredicate is not None:
if re.match(r'^\s*\|', line) is not None:
phrase += ('' if collecting_table_data else '\n') + line
collecting_table_data = True
continue
elif phrase is not None and phrase != '':
collecting_table_data = False
self.addPhrase(
activePredicate, phrase, feature_name, file_name)

match_on = self.get_match_on(activePredicate)
match = re.match(
r'^\s*(?:' + match_on + ')\s+(.*)$', line, re.IGNORECASE)
if match is not None:
if match.group('given'):
activePredicate = 'given'
elif match.group('when') is not None:
activePredicate = 'when'
elif match.group('then') is not None:
activePredicate = 'then'

phrase = match.group(4)
else:
activePredicate = None

def get_feature_files(self, dir_name, *args):
fileList = []

for file in os.listdir(dir_name):
dirfile = os.path.join(dir_name, file)
if os.path.isfile(dirfile):
fileName, fileExtension = os.path.splitext(dirfile)
if fileExtension == '.feature':
fileList.append(dirfile)
elif os.path.isdir(dirfile):
fileList += self.get_feature_files(dirfile, *args)

return fileList
18 changes: 17 additions & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,28 @@ These settings can be found in Base File.sublime-settings
- **table_cleaner_align_to_middle** - Align the text of each cell to middle (if set to false, the text will be alligned to left) - default: **false**
- **table_cleaner_delimiters_white_spaces** - The number of whitespaces between the text of a cell and the delimiters - default: **1**

## Gherkin Auto-complete

Shows all existing gherkin phrases within features in folder hierarchy.

![Gherkin autocompletion](https://dl.dropboxusercontent.com/u/4031118/sublime-gherkin-autocomplete/sublime-gherkin-autocomplete.png)

*sublime-gherkin-auto-complete and cucumber-sublime-bundle plugins are mutually exclusive, so if you have
sublime-gherkin-auto-complete installed and want to install cucumber-sublime-bundle you have to uninstall
sublime-gherkin-auto-complete first.*

## Credits
Created by the Github user @sagework, who pulled his/her repo from https://github.com/sagework/cucumber-sublime2-bundle

The Table Cleaner plug-in has been kindly contributed by [@amisarca](https://github.com/amisarca). If you'd like just the plug-in, without the rest of this Cucumber bundle, see his repositories:
The Table Cleaner plug-in has been kindly contributed by [@amisarca](https://github.com/amisarca).
If you'd like just the plug-in, without the rest of this Cucumber bundle, see his repositories:

* ST2: https://github.com/amisarca/Sublime-Text-2-Table-Cleaner
* ST3: https://github.com/amisarca/Sublime-Text-Table-Cleaner

The Gherkin auto-complete plug-in has been kindly contributed by [@AndyHitchman](https://github.com/AndyHitchman/).
If you'd like just the plug-in, without the rest of this Cucumber bundle, see his
[repository](https://github.com/AndyHitchman/sublime-gherkin-auto-complete).


I host this project at https://github.com/drewda/cucumber-sublime-bundle for those who use Sublime Package Control.

0 comments on commit 33b951e

Please sign in to comment.