diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..cc1923a --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.8 diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c664f0..54a5eb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Wording Status +## Unreleased + +- Add `Command Palette` commands to open settings files `Preferences: WordingStatus Settings` or `WordingStatus Settings: User`/`WordingStatus Settings: Default` +- 🐞 don't leave old statusbar messages when changing its position via `status_order_prefix` +- moved to the new 3.8 Sublime Text plugin host +- add `minute_separator` user setting for separating `5m 2s` in time +- add `in_group_separator` user setting for separating line/word/char counts +- add `group_separator` user setting for separating groups of counts (via a template) +- add user settings to reorder count groups / individual counts via a `status_template` template string + ## 2.0.1 Created the setting `status_order_prefix` Evgeny (eugenesvk). diff --git a/Default.sublime-commands b/Default.sublime-commands new file mode 100644 index 0000000..30502f6 --- /dev/null +++ b/Default.sublime-commands @@ -0,0 +1,8 @@ +[ +{"caption":"Preferences: WordingStatus Settings" ,"command": "edit_settings" ,"args":{ + "base_file":"${packages}/WordingStatus/WordingStatus.sublime-settings", + "user_file":"${packages}/User/WordingStatus.sublime-settings", + "default" :"{\n\t$0\n}\n"}}, +{"caption":"Preferences: WordingStatus Settings: User" ,"command":"open_file" ,"args":{"file":"${packages}/User/WordingStatus.sublime-settings"}}, +{"caption":"Preferences: WordingStatus Settings: Default" ,"command":"open_file" ,"args":{"file":"${packages}/WordingStatus/WordingStatus.sublime-settings"}}, +] diff --git a/README.md b/README.md index e222d3a..ed69ed7 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,10 @@ See also: ## Preferences -Located under Sublime Text>Preferences>Package Settings>Settings — User -(You probably need to copy the default settings from the uneditable Sublime Text>Preferences>Package Settings>Settings — **Default**) +Menu `Sublime Text`>`Settings…`>`Package Settings`>`WordingStatus`>`Settings` opens both the default and user settings files, where you can see every option explained in comments and copy/change the ones you need to adjust. + +Or use the `Command Palette` commands `Preferences: WordingStatus Settings` or `WordingStatus Settings: User`/`WordingStatus Settings: Default` ## Inspiration diff --git a/WordingStatus.py b/WordingStatus.py index 7b4a1c3..ab3b9f3 100644 --- a/WordingStatus.py +++ b/WordingStatus.py @@ -1,4 +1,3 @@ - import sublime import sublime_plugin @@ -6,409 +5,403 @@ import time import threading -from math import ceil as ceil -from os.path import basename +from math import ceil as ceil +from os.path import basename -VIEW_SIZE_LIMIT = 4194304 +VIEW_SIZE_LIMIT = 4194304 -Preferences = {} -g_sleepEvent = threading.Event() -g_is_already_running = False +Pref = {} +g_sleepEvent = threading.Event() +g_is_already_running = False def plugin_unloaded(): - global Preferences - global g_is_already_running - - g_is_already_running = False - sublime_settings.clear_on_change( 'WordingStatus' ) + global g_is_already_running - for window in sublime.windows(): + g_is_already_running = False + subl_setting.clear_on_change('WordingStatus') - for view in window.views(): - view.erase_status(Preferences.status_name); + for window in sublime.windows(): + for view in window.views(): + view.erase_status(Pref.status_name); def plugin_loaded(): - global Preferences - global sublime_settings + global Pref + global subl_setting - sublime_settings = sublime.load_settings( 'WordingStatus.sublime-settings' ) - Preferences.load(); + subl_setting = sublime.load_settings('WordingStatus.sublime-settings') + Pref.load(); - sublime_settings.clear_on_change( 'WordingStatus' ) - sublime_settings.add_on_change( 'WordingStatus', lambda: Preferences.load() ) + subl_setting.clear_on_change('WordingStatus') + subl_setting.add_on_change( 'WordingStatus', lambda:Pref.load()) - # Initialize the WordingStatuses's countView attribute - WordingStatuses.setUpView( get_active_view() ) + WordingStatuses.setUpView(get_active_view()) # Initialize the WordingStatuses's countView attribute - if not g_is_already_running: - g_sleepEvent.set() - - # Wait the Preferences class to be loaded - sublime.set_timeout_async( configure_word_count, 5000 ) + if not g_is_already_running: + g_sleepEvent.set() + sublime.set_timeout_async(configure_word_count, 5000) # Wait the Pref class to be loaded def configure_word_count(): - """ - break/interrupt a time.sleep() in python - https://stackoverflow.com/questions/5114292/break-interrupt-a-time-sleep-in-python - """ - global g_is_already_running - g_is_already_running = True + """ + break/interrupt a time.sleep() in python + https://stackoverflow.com/questions/5114292/break-interrupt-a-time-sleep-in-python """ + global g_is_already_running + g_is_already_running = True - # Reset the internal flag to false. Subsequently, threads calling wait() will block until set() - # is called to set the internal flag to true again. - g_sleepEvent.clear() + g_sleepEvent.clear() # Reset the internal flag to false. Subsequently, threads calling wait() will block until set() is called to set the internal flag to true again. - thread = threading.Thread( target=word_count_loop ) - thread.start() + thread = threading.Thread(target=word_count_loop) + thread.start() def word_count_loop(): - mininum_time = 0.01 - default_time = 3.0 - - while True: - # Stops the thread when the plugin is reloaded or unloaded - if not g_is_already_running: - break + mininum_time = 0.01 + default_time = 3.0 - # sleep time is adaptive, if takes more than `mininum_time` to calculate the word count, - # sleep_time becomes `elapsed_time*3` - if not Preferences.is_already_running: + while True: + if not g_is_already_running: # Stops the thread when the plugin is reloaded or unloaded + break - if g_sleepEvent.is_set(): - # set g_sleepEvent._flag, a.k.a., g_sleepEvent.is_set() to False - g_sleepEvent.clear() - WordingStatuses.setUpView( WordingStatuses.activeView ) + if not Pref.is_already_running: # sleep time is adaptive, if takes more than `mininum_time` to calculate the word count, sleep_time becomes `elapsed_time*3` + if g_sleepEvent.is_set(): # set g_sleepEvent._flag, a.k.a., g_sleepEvent.is_set() to False + g_sleepEvent.clear() + WordingStatuses.setUpView(WordingStatuses.activeView) - WordingStatuses.doCounting() + WordingStatuses.doCounting() - # print( "word_count_loop, elapsed_time: %f microseconds" % ( Preferences.elapsed_time * 1000 ) ) - g_sleepEvent.wait( Preferences.elapsed_time*100 if Preferences.elapsed_time > mininum_time else default_time ) + # print("word_count_loop, elapsed_time: %f microseconds" % (Pref.elapsed_time * 1000)) + g_sleepEvent.wait(Pref.elapsed_time*100 if Pref.elapsed_time > mininum_time else default_time) +re_group = re.compile(r'\|') +valid_id = ['l','w','c','W','C','p','t'] -class Preferences(): +class Pref(): - @staticmethod - def load(): - Preferences.elapsed_time = 1.4 - Preferences.is_already_running = False + @staticmethod + def load(): + Pref.elapsed_time = 1.4 + Pref.is_already_running = False - Preferences.wordRegex = re.compile( sublime_settings.get('word_regexp', "^[^\w]?`*\w+[^\w]*$"), re.U ) - Preferences.wordRegex = Preferences.wordRegex.match - Preferences.splitRegex = sublime_settings.get('word_split', None) + Pref.wordRegex = re.compile(subl_setting.get('word_regexp',r"^[^\w]?`*\w+[^\w]*$"), re.U) + Pref.wordRegex = Pref.wordRegex.match + Pref.splitRegex = subl_setting.get('word_split', None) - if Preferences.splitRegex: - Preferences.splitRegex = re.compile(Preferences.splitRegex, re.U) - Preferences.splitRegex = Preferences.splitRegex.findall + if Pref.splitRegex: + Pref.splitRegex = re.compile(Pref.splitRegex, re.U) + Pref.splitRegex = Pref.splitRegex.findall - Preferences.status_name = sublime_settings.get('status_order_prefix', '') + 'WordCountStatus' - - Preferences.enable_readtime = sublime_settings.get('enable_readtime', False) - Preferences.enable_count_lines = sublime_settings.get('enable_count_lines', False) - Preferences.enable_count_chars = sublime_settings.get('enable_count_chars', False) - Preferences.enable_count_pages = sublime_settings.get('enable_count_pages', False) - Preferences.enable_count_words = sublime_settings.get('enable_count_words', True) - Preferences.file_size_limit = sublime_settings.get('file_size_limit', VIEW_SIZE_LIMIT) - - Preferences.enable_line_word_count = sublime_settings.get('enable_line_word_count', False) - Preferences.enable_line_char_count = sublime_settings.get('enable_line_char_count', False) - - Preferences.readtime_wpm = sublime_settings.get('readtime_wpm', 200) - Preferences.words_per_page = sublime_settings.get('words_per_page', 300) - Preferences.char_ignore_whitespace = sublime_settings.get('char_ignore_whitespace', True) - Preferences.whitelist_syntaxes = sublime_settings.get('whitelist_syntaxes', []) - Preferences.blacklist_syntaxes = sublime_settings.get('blacklist_syntaxes', []) - Preferences.strip = sublime_settings.get('strip', []) + old_status_name = Pref.status_name if hasattr(Pref,'status_name') else '' + Pref.status_name = subl_setting.get('status_order_prefix', '') + 'WordingStatus' + if old_status_name \ + and not old_status_name == Pref.status_name: # erase previous status on ID change + for window in sublime.windows(): + for view in window.views(): + view.set_status(old_status_name,''); - Preferences.thousands_separator = sublime_settings.get('thousands_separator' , "." ) + Pref.status_template = subl_setting.get('status_template' , 'lwc|WC|p|t') + Pref.status_groups = re.split(re_group,Pref.status_template) # [lwc,WC,p,t] - Preferences.label_line = sublime_settings.get('label_line' , " Lines" ) - Preferences.label_word = sublime_settings.get('label_word' , " Words" ) - Preferences.label_char = sublime_settings.get('label_char' , " Chars" ) - Preferences.label_word_in_line = sublime_settings.get('label_word_in_line', " Words in lines" ) - Preferences.label_char_in_line = sublime_settings.get('label_char_in_line', " Chars in lines" ) - Preferences.label_time = sublime_settings.get('label_time' , " reading time" ) - Preferences.label_page = sublime_settings.get('label_page' , "Page " ) + Pref.enable_readtime = subl_setting.get('enable_readtime' , False) + Pref.enable_count_lines = subl_setting.get('enable_count_lines', False) + Pref.enable_count_chars = subl_setting.get('enable_count_chars', False) + Pref.enable_count_pages = subl_setting.get('enable_count_pages', False) + Pref.enable_count_words = subl_setting.get('enable_count_words', True) + Pref.file_size_limit = subl_setting.get('file_size_limit' , VIEW_SIZE_LIMIT) - Preferences.page_count_mode_count_words = sublime_settings.get('page_count_mode_count_words', True) + Pref.enable_line_word_count = subl_setting.get('enable_line_word_count', False) + Pref.enable_line_char_count = subl_setting.get('enable_line_char_count', False) + Pref.readtime_wpm = subl_setting.get('readtime_wpm' , 200) + Pref.words_per_page = subl_setting.get('words_per_page' , 300) + Pref.char_ignore_whitespace = subl_setting.get('char_ignore_whitespace', True) + Pref.whitelist_syntaxes = subl_setting.get('whitelist_syntaxes' , []) + Pref.blacklist_syntaxes = subl_setting.get('blacklist_syntaxes' , []) + Pref.strip = subl_setting.get('strip' , []) -class WordingStatuses(sublime_plugin.EventListener): - countView = None - activeView = None - wordCountViews = {} + Pref.thousands_separator = subl_setting.get('thousands_separator' , ".") + Pref.minute_separator = subl_setting.get('minute_separator' , " ") + Pref.in_group_separator = subl_setting.get('in_group_separator' , " ") + Pref.group_separator = subl_setting.get('group_separator' , ", ") - def on_close(self, view): - view_id = view.id() + Pref.label_line = subl_setting.get('label_line' , " Lines" ) + Pref.label_word = subl_setting.get('label_word' , " Words" ) + Pref.label_char = subl_setting.get('label_char' , " Chars" ) + Pref.label_word_in_line = subl_setting.get('label_word_in_line', " Words in lines" ) + Pref.label_char_in_line = subl_setting.get('label_char_in_line', " Chars in lines" ) + Pref.label_time = subl_setting.get('label_time' , " reading time" ) + Pref.label_page = subl_setting.get('label_page' , "Page " ) - if view_id in WordingStatuses.wordCountViews: - del WordingStatuses.wordCountViews[view_id] + Pref.page_count_mode_count_words = subl_setting.get('page_count_mode_count_words', True) - def on_selection_modified_async(self, view): - if Preferences.enable_count_words: - selections = view.sel() +class WordingStatuses(sublime_plugin.ViewEventListener): + countView = None + activeView = None + wordCountViews = {} - for selection in selections: + def on_close(self): + view = self.view + view_id = view.id() - if len( selection ): - WordingStatuses.countView.is_text_selected = True - return + if view_id in WordingStatuses.wordCountViews: + del WordingStatuses.wordCountViews[view_id] - WordingStatuses.countView.is_text_selected = False + def on_selection_modified_async(self): + view = self.view - def on_activated_async(self, view): - # print( "on_activated_async, view_id: %d" % view.id() ) - WordingStatuses.activeView = view - g_sleepEvent.set() + if Pref.enable_count_words: + selections = view.sel() - @classmethod - def doCounting(cls): - countView = cls.countView + for selection in selections: - if countView.view.change_count() != countView.change_count \ - or countView.is_text_selected: + if len(selection): + WordingStatuses.countView.is_text_selected = True + return - countView.startCounting() + WordingStatuses.countView.is_text_selected = False - @classmethod - def setUpView(cls, view): - view_settings = view.settings() - wordCountViews = cls.wordCountViews + def on_activated_async(self): + view = self.view + # print("on_activated_async, view_id: %d" % view.id()) + WordingStatuses.activeView = view + g_sleepEvent.set() - if view_settings.get('is_widget'): - _view = get_active_view() + @classmethod + def doCounting(cls): + countView = cls.countView - if _view: - view = _view - view_settings = view.settings() + if countView.view.change_count() != countView.change_count \ + or countView.is_text_selected: - syntax, is_enabled = cls.should_run_with_syntax( view_settings ) - view_id = view.id() + countView.startCounting() - # print( "setUpView, view_id: %d" % view_id ) - if view_id in wordCountViews: - wordCountView = wordCountViews[view_id] - wordCountView.syntax = syntax - wordCountView.syntax = is_enabled + @classmethod + def setUpView(cls, view): + view_settings = view.settings() + wordCountViews = cls.wordCountViews - else: - wordCountView = WordingStatusesView( view, syntax, is_enabled ) - wordCountViews[view_id] = wordCountView + if view_settings.get('is_widget'): + _view = get_active_view() - cls.countView = wordCountView + if _view: + view = _view + view_settings = view.settings() - @staticmethod - def should_run_with_syntax(view_settings): - syntax = view_settings.get('syntax') - syntax = basename( syntax ).split( '.' )[0].lower() if syntax != None else "plain text" + syntax, is_enabled = cls.should_run_with_syntax(view_settings) + view_id = view.id() - if len( Preferences.blacklist_syntaxes ) > 0: + # print("setUpView, view_id: %d" % view_id) + if view_id in wordCountViews: + wordCountView = wordCountViews[view_id] + wordCountView.syntax = syntax + wordCountView.syntax = is_enabled - for white in Preferences.blacklist_syntaxes: + else: + wordCountView = WordingStatusesView(view, syntax, is_enabled) + wordCountViews[view_id] = wordCountView - if white == syntax: - return syntax, False + cls.countView = wordCountView - if len(Preferences.whitelist_syntaxes) > 0: + @staticmethod + def should_run_with_syntax(view_settings): + syntax = view_settings.get('syntax') + syntax = basename(syntax).split('.')[0].lower() if syntax != None else "plain text" - for white in Preferences.whitelist_syntaxes: + if len(Pref.blacklist_syntaxes) > 0: + for white in Pref.blacklist_syntaxes: + if white == syntax: + return syntax, False - if white == syntax: - return syntax, True + if len(Pref.whitelist_syntaxes) > 0: + for white in Pref.whitelist_syntaxes: + if white == syntax: + return syntax, True - return syntax, False + return syntax, False - return syntax, True + return syntax, True class WordingStatusesView(): - def __init__(self, view, syntax, is_enabled): - self.syntax = syntax - self.is_enabled = is_enabled - self.is_text_selected = False - - # We need to set it to -1, because by default it starts on 0. Then we for an update when a - # view is first activated by `WordingStatuses::on_activated_async()` - self.change_count = -1 - self.lines_contents = [] - - self.view = view - self.contents = [] + def __init__(self, view, syntax, is_enabled): + self.syntax = syntax + self.is_enabled = is_enabled + self.is_text_selected = False - self.char_count = 0 - self.word_count = 0 - self.line_count = 0 + # We need to set it to -1, because by default it starts on 0. Then we for an update when a view is first activated by `WordingStatuses::on_activated_async()` + self.change_count = -1 + self.lines_contents = [] - self.word_count_line = 0 - self.char_count_line = 0 + self.view = view + self.contents = [] - def updateViewContents(self): - view = self.view - del self.contents[:] + self.char_count = 0 + self.word_count = 0 + self.line_count = 0 - selections = view.sel() - view_size = view.size() + self.word_count_line = 0 + self.char_count_line = 0 - if Preferences.enable_line_char_count or Preferences.enable_line_word_count: - del self.lines_contents[:] + def updateViewContents(self): + view = self.view + del self.contents[:] - for selection in selections: - self.lines_contents.append( view.substr( view.line( selection.end() ) ) ) + selections = view.sel() + view_size = view.size() - file_size_limit = Preferences.file_size_limit - is_limited = view_size > file_size_limit + if Pref.enable_line_char_count or Pref.enable_line_word_count: + del self.lines_contents[:] - if is_limited: - self.is_text_selected = False + for selection in selections: + self.lines_contents.append(view.substr(view.line(selection.end()))) - if self.is_text_selected: + file_size_limit = Pref.file_size_limit + is_limited = view_size > file_size_limit - for selection in selections: - self.contents.append( view.substr( selection ) ) + if is_limited: + self.is_text_selected = False - else: - self.contents.append( view.substr( sublime.Region( 0, file_size_limit if is_limited else view_size ) ) ) + if self.is_text_selected: - def startCounting(self): + for selection in selections: + self.contents.append(view.substr(selection)) - if not self.is_enabled: - return - - Preferences.start_time = time.perf_counter() - Preferences.is_already_running = True - - view = self.view - self.updateViewContents() + else: + self.contents.append(view.substr(sublime.Region(0, file_size_limit if is_limited else view_size))) - if self.syntax and self.syntax in Preferences.strip: + def startCounting(self): - for regular_expression in Preferences.strip[self.syntax]: - lines_count = len( self.contents ) - lines_contents_count = len( self.lines_contents ) + if not self.is_enabled: + return - for selection_index in range( lines_count ): - self.contents[selection_index] = re.sub( regular_expression, '', self.contents[selection_index] ) + Pref.start_time = time.perf_counter() + Pref.is_already_running = True - for selection_index in range( lines_contents_count ): - self.lines_contents[selection_index] = re.sub( regular_expression, '', self.lines_contents[selection_index] ) + view = self.view + self.updateViewContents() - if Preferences.enable_count_lines: - self.line_count = view.rowcol( view.size() )[0] + 1 + if self.syntax and self.syntax in Pref.strip: - if Preferences.enable_count_words: - self.word_count = count_words( self.contents ) + for regular_expression in Pref.strip[self.syntax]: + lines_count = len(self.contents ) + lines_contents_count = len(self.lines_contents) - if Preferences.enable_count_chars: - self.char_count = count_chars( self.contents ) + for selection_index in range(lines_count): + self.contents [selection_index] = re.sub(regular_expression, '', self.contents [selection_index]) - if Preferences.enable_line_char_count: - self.char_count_line = count_chars( self.lines_contents ) + for selection_index in range(lines_contents_count): + self.lines_contents[selection_index] = re.sub(regular_expression, '', self.lines_contents[selection_index]) - if Preferences.enable_line_word_count: - self.word_count_line = count_words( self.lines_contents ) + if Pref.enable_count_lines: + self.line_count = view.rowcol(view.size())[0] + 1 + if Pref.enable_count_words: + self.word_count = count_words(self.contents ) + if Pref.enable_count_chars: + self.char_count = count_chars(self.contents ) + if Pref.enable_line_char_count: + self.char_count_line = count_chars(self.lines_contents) + if Pref.enable_line_word_count: + self.word_count_line = count_words(self.lines_contents) - self.displayCountResults() + self.displayCountResults() - def displayCountResults(self): - display( self.view, self.word_count, self.char_count, self.line_count, self.word_count_line, self.char_count_line ) + def displayCountResults(self): + display(self.view, self.word_count, self.char_count, self.line_count, self.word_count_line, self.char_count_line) - Preferences.elapsed_time = time.perf_counter() - Preferences.start_time - Preferences.is_already_running = False + Pref.elapsed_time = time.perf_counter() - Pref.start_time + Pref.is_already_running = False def display(view, word_count, char_count, line_count, word_count_line, char_count_line): - status = [] - seconds = int( word_count % Preferences.readtime_wpm / ( Preferences.readtime_wpm / 60 ) ) - k_sep = Preferences.thousands_separator - - if line_count > 0: - status.append( "{:,}{}".format( line_count , Preferences.label_line ).replace(',',k_sep) ) - - if word_count > 0: - status.append( "{:,}{}".format( word_count , Preferences.label_word ).replace(',',k_sep) ) - - if char_count > 0: - status.append( "{:,}{}".format( char_count , Preferences.label_char ).replace(',',k_sep) ) - - if word_count_line > 0: - status.append( "{:,}{}".format( word_count_line, Preferences.label_word_in_line ).replace(',',k_sep) ) - - if char_count_line > 0: - status.append( "{:,}{}".format( char_count_line, Preferences.label_char_in_line ).replace(',',k_sep) ) - - if Preferences.enable_count_pages and word_count > 0: - - if not Preferences.page_count_mode_count_words or Preferences.words_per_page < 1: - visible = view.visible_region() - rows_per_page = (view.rowcol(visible.end())[0]) - (view.rowcol(visible.begin())[0]) - pages = ceil((view.rowcol(view.size()-1)[0] + 1 ) / rows_per_page) - current_line = view.rowcol(view.sel()[0].begin())[0]+1 - current_page = ceil(current_line / rows_per_page) + status = [] + seconds = int(word_count % Pref.readtime_wpm / (Pref.readtime_wpm / 60)) + k_sep = Pref.thousands_separator + + out_line,pos_line,out_word,pos_word,out_char,out_word_line,pos_word_line = '','','','','','','' + + out_str = dict() + + out_str['l'] = "{:,}{}".format(line_count ,Pref.label_line ).replace(',',k_sep) if line_count else '' + out_str['w'] = "{:,}{}".format(word_count ,Pref.label_word ).replace(',',k_sep) if word_count else '' + out_str['c'] = "{:,}{}".format(char_count ,Pref.label_char ).replace(',',k_sep) if char_count else '' + out_str['W'] = "{:,}{}".format(word_count_line ,Pref.label_word_in_line ).replace(',',k_sep) if word_count_line else '' + out_str['C'] = "{:,}{}".format(char_count_line ,Pref.label_char_in_line ).replace(',',k_sep) if char_count_line else '' + for id_s in valid_id: + if id_s in out_str: + continue + else: + out_str[id_s] = '' # prefill to have all valid ids as keys + + if Pref.enable_count_pages and word_count > 0: + if not Pref.page_count_mode_count_words or Pref.words_per_page < 1: + visible = view.visible_region() + rows_per_page = (view.rowcol(visible.end())[0]) - (view.rowcol(visible.begin())[0]) + pages = ceil((view.rowcol(view.size()-1)[0] + 1) / rows_per_page) + current_line = view.rowcol(view.sel()[0].begin())[0]+1 + current_page = ceil(current_line / rows_per_page) + else: + pages = ceil(word_count / Pref.words_per_page) + rows = view.rowcol(view.size()-1)[0] + 1 + current_line = view.rowcol(view.sel()[0].begin())[0]+1 + current_page = ceil((current_line / Pref.words_per_page) / (rows / Pref.words_per_page)) - else: - pages = ceil(word_count / Preferences.words_per_page) - rows = view.rowcol(view.size()-1)[0] + 1 - current_line = view.rowcol(view.sel()[0].begin())[0]+1 - current_page = ceil((current_line / Preferences.words_per_page) / (rows / Preferences.words_per_page)) + if pages > 1: + out_str['p'] = "{}{:,}/{:,}".format(Pref.label_page, current_page, pages).replace(',',k_sep) - if pages > 1: - status.append( "{}{:,}/{:,}".format( Preferences.label_page, current_page, pages ).replace( ',', k_sep ) ) + if Pref.enable_readtime and seconds >= 1: + minutes = int(word_count / Pref.readtime_wpm) + out_str['t'] = "~{:,}m{}{:,}s{}".format( minutes, Pref.minute_separator, seconds, Pref.label_time).replace(',',k_sep) - if Preferences.enable_readtime and seconds >= 1: - minutes = int( word_count / Preferences.readtime_wpm ) - status.append( "~{:,}m {:,}s{}".format( minutes, seconds, Preferences.label_time ).replace( ',', k_sep ) ) + # print(f"out_str={out_str}") + for group in Pref.status_groups: + g_out = [] + for i,id_s in enumerate(group): # lwc + if id_s in valid_id and out_str[id_s]: # l + g_out.append(out_str[id_s]) # ← line count + if g_out: + status.append((Pref.in_group_separator).join(g_out)) - status_text = ', '.join( status ) - view.set_status( Preferences.status_name, status_text ) - # print( "view: %d, Setting status to: " % view.id() + status_text ) + status_text = (Pref.group_separator).join(status) + view.set_status(Pref.status_name, status_text) + # print("view: %d, Setting status to: " % view.id() + status_text) def count_words(text_list): - words_count = 0 - - wordRegex = Preferences.wordRegex - splitRegex = Preferences.splitRegex + words_count = 0 - if splitRegex: + wordRegex = Pref.wordRegex + splitRegex = Pref.splitRegex - for text in text_list: - words = splitRegex( text ) + if splitRegex: + for text in text_list: + words = splitRegex(text) + for word in words: + if wordRegex(word): + words_count += 1 + else: + for text in text_list: + words_count += len(text.split()) - for word in words: - - if wordRegex( word ): - words_count += 1 - - else: - - for text in text_list: - words_count += len( text.split() ) - - return words_count + return words_count def count_chars(text_list): - char_count = 0 + char_count = 0 - if Preferences.char_ignore_whitespace: - char_count = sum( sum( len( word ) for word in words.split() ) for words in text_list ) + if Pref.char_ignore_whitespace: + char_count = sum(sum(len(word) for word in words.split()) for words in text_list) + else: + char_count = sum( len(words) for words in text_list) - else: - char_count = sum( len( words ) for words in text_list ) - - return char_count + return char_count def get_active_view(): - window = sublime.active_window() - - if window: - return window.active_view() - - return None + window = sublime.active_window() + if window: + return window.active_view() + return None diff --git a/WordingStatus.sublime-settings b/WordingStatus.sublime-settings index 3db4bd2..b495b70 100644 --- a/WordingStatus.sublime-settings +++ b/WordingStatus.sublime-settings @@ -1,31 +1,42 @@ -{//Key Value |Default| Comment -"enable_readtime" : false , // |false| Allows you to control if the estimated reading time is enabled. Reading time is only displayed when there is a time > 1s -"enable_count_words" : true , // |true| Display the number of words in file -"enable_count_lines" : true , // |true| Display the number of lines in file -"enable_count_chars" : true , // |true| Display the number of characters in file -"enable_count_pages" : false , // |false| Display the number of pages in file -"words_per_page" : 360 , // |360| Sets the number of words per page used to calculate number of pages -"file_size_limit" : 4194304 , // |4194304| The maximum characters a file to be computed can have -"readtime_wpm" : 200 , // |200| Sets the WPM to calculate the Estimated Reading Time for the file -"char_ignore_whitespace" : true , // |true| Whether to skip whitespace for the character count -"page_count_mode_count_words" : false , // |false| Sets the page count mode to words per page -"enable_line_word_count" : false , // |false| Display the count of words found on current line -"enable_line_char_count" : false , // |false| Display the count of characters found on current line -"whitelist_syntaxes" : [] , // |[]| An array of syntax names that WordingStatus should run on - // Example: ["Plain text", "Markdown"] If the array is empty, like it is by default, WordingStatus will run on any syntax -"blacklist_syntaxes" : [] , // |[]| An array of syntax names that WordingStatus should not run on Example: ["Plain text", "Markdown"] If the array is empty, like it is by default, WordingStatus will run on any syntax -"word_regexp" : "" , // |""| Word Regular expression. Defaults empty, an internal regular expression is used. If the portion of text matches this RegExp then the word is counted -"word_split" : "" , // |""| Split portions of text to test later as words with a Regular expression. Defaults to String.split() with no arguments, means that content will trim() and empty values (all whitespaces) are not used. In case of containing some value different than empty, the return of "re.findall" will be used -"thousands_separator" : "." , // |"."| Thousands separator's symbol -"status_order_prefix" : "" , // |""| Prefix the status name to change its order in the status bar, which uses alphabetical sorting +{//Key Value ≝Default Comment +"enable_readtime" : false , //false Allows you to control if the estimated reading time is enabled. Reading time is only displayed when there is a time > 1s +"enable_count_words" : true , //true Display the number of words in file +"enable_count_lines" : true , //true Display the number of lines in file +"enable_count_chars" : true , //true Display the number of characters in file +"enable_count_pages" : false , //false Display the number of pages in file +"words_per_page" : 360 , //360 Sets the number of words per page used to calculate number of pages +"file_size_limit" : 4194304 , //4194304 The maximum characters a file to be computed can have +"readtime_wpm" : 200 , //200 Sets the WPM to calculate the Estimated Reading Time for the file +"char_ignore_whitespace" : true , //true Whether to skip whitespace for the character count +"page_count_mode_count_words" : false , //false Sets the page count mode to words per page +"enable_line_word_count" : false , //false Display the count of words found on current line +"enable_line_char_count" : false , //false Display the count of characters found on current line +"whitelist_syntaxes" : [] , //[] An array of syntax names that WordingStatus should run on + // Example: ["Plain text", "Markdown"] If the array is empty, like it is by default, WordingStatus will run on any syntax +"blacklist_syntaxes" : [] , //[] An array of syntax names that WordingStatus should not run on Example: ["Plain text", "Markdown"] If the array is empty, like it is by default, WordingStatus will run on any syntax +"word_regexp" : "" , //"" Word Regular expression. Defaults empty, an internal regular expression is used. If the portion of text matches this RegExp then the word is counted +"word_split" : "" , //"" Split portions of text to test later as words with a Regular expression. Defaults to String.split() with no arguments, means that content will trim() and empty values (all whitespaces) are not used. In case of containing some value different than empty, the return of "re.findall" will be used +"thousands_separator" : "." , //"." Thousands separator's symbol +"minute_separator" : " " , //" " Symbol separating minutes and seconds +"in_group_separator" : " " , //" " Symbol separating line/word/char counts +"group_separator" : ", " , //", " Symbol separating groups +// Reorder indicators +"status_order_prefix" : "" , //"" Prefix the status name to change its order in the status bar, which uses alphabetical sorting +"status_template" : "lwc|WC|p|t" , //"lwc|WC|p|t" Order of various counts in the statusbar (CaSe sensitive!) + // lwc line / word / char counts + // WC Word / Char per line counts + // p page + // t time + // | group separator -"label_line" : " Lines" , // |" Lines"| Label for the number of lines -"label_word" : " Words" , // |" Words"| Label for the number of words -"label_char" : " Chars" , // |" Chars"| Label for the number of chars -"label_word_in_line" : " Words in lines" , // |" Words in lines"| Label for the number of words in lines -"label_char_in_line" : " Chars in lines" , // |" Chars in lines"| Label for the number of chars in lines -"label_time" : " reading time" , // |" reading time"| Label for the reading time -"label_page" : "Page " , // |"Page "| Label for the page number +//Key Value ≝Default Comment +"label_line" : " Lines" , //" Lines" Label for the number of lines +"label_word" : " Words" , //" Words" Label for the number of words +"label_char" : " Chars" , //" Chars" Label for the number of chars +"label_word_in_line" : " Words in lines" , //" Words in lines" Label for the number of words in lines +"label_char_in_line" : " Chars in lines" , //" Chars in lines" Label for the number of chars in lines +"label_time" : " reading time" , //" reading time" Label for the reading time +"label_page" : "Page " , //"Page " Label for the page number // Remove regex patterns by syntax. Split portions of text to test later as words with a Regular expression. Defaults to String.split() with no arguments, means that content will trim() and empty values (all whitespaces) are not used. In case of containing some value different than empty, the return of "re.findall" will be used "strip": { // Please use lowercase for the syntax names in the following section