def handle_midi_input(msg): # Only handle the message if the piano has the focus; could also find the # piano view in the window as the other command does. Note: there is not # always an active view. view = sublime.active_window().active_view() if not view: return listener = sublime_plugin.find_view_event_listener(view, Piano) if listener: # Ship the message over; this will play notes, but also allow for # program changes, etc. This lets incoming velocity and aftertouch # information through without the event listener needing to synthesize # them out_port.send(msg) # For note messges, we want to synthesize the display. if msg.type.startswith('note_'): octave, note = PianoMidi.midi_note_to_note(msg.note) # Per the specs, note_on with a velocity of 0 should be interpreted # as note_off; if that happens replace the message so the display # will update. if msg.type == 'note_on' and msg.velocity == 0: msg = mido.Message('note_off', note=msg.note, time=msg.time) # Get the listener to update the display but not play the note. sublime.set_timeout(lambda: getattr(listener, msg.type)(octave, note, False)) return True return False
def run(self, edit): view = self.view cursor = view.sel()[0] listener = sublime_plugin.find_view_event_listener( view, SyntaxTestHighlighterListener) details = listener.get_details_of_test_assertion_line(cursor.begin()) if not details.comment_marker_match: return # find the last test assertion column on the previous line details = listener.get_details_of_test_assertion_line( details.line_region.begin() - 1) next_col = None if not details.assertion_colrange: # the previous line wasn't a syntax test line, so instead # find the first non-whitespace char on the line being tested above for pos in range(details.line_region.begin(), details.line_region.end()): if view.substr(pos).strip() != '': break next_col = view.rowcol(pos)[1] else: next_col = details.assertion_colrange[1] col_diff = next_col - view.rowcol(cursor.begin())[1] view.insert(edit, cursor.end(), " " * col_diff) view.run_command('suggest_syntax_test')
def run(self, edit): view = self.view cursor = view.sel()[0] listener = sublime_plugin.find_view_event_listener(view, SyntaxTestHighlighterListener) details = listener.get_details_of_test_assertion_line(cursor.begin()) if not details.comment_marker_match: return # find the last test assertion column on the previous line details = listener.get_details_of_test_assertion_line(details.line_region.begin() - 1) next_col = None skip_whitespace = get_setting('syntax_test.skip_whitespace', True) if details.assertion_colrange: next_col = details.assertion_colrange[1] else: # the previous line wasn't a syntax test line, so instead # start at the first position on the line. We will then # advance to the first non-whitespace char on the line. next_col = 0 skip_whitespace = True # find the next non-whitespace char on the line being tested above if skip_whitespace: line_region = listener.get_details_of_line_being_tested()[1] pos = line_region.begin() + next_col for pos in range(pos, line_region.end()): if view.substr(pos).strip() != '': break next_col = view.rowcol(pos)[1] col_diff = next_col - view.rowcol(cursor.begin())[1] view.insert(edit, cursor.end(), " " * col_diff) view.run_command('packagedev_suggest_syntax_test')
def run(self, edit, character): listener = sublime_plugin.find_view_event_listener(self.view, Piano) try: index = piano_prefs('keyboard_keys').index(character) except ValueError: return # left most key in the list starts at octave 3 # TODO: base this on how many keys are in the list? # or on the piano's start octave? # - ideally we would show the keys on the piano to make it clear octave = 3 + index // len(PianoMidi.notes_solfege) note_index = index % len(PianoMidi.notes_solfege) note = (octave, note_index) # if the note is already playing, just extend the time out rather than playing it again if note in self.active_notes.keys(): self.active_notes[note] += 1 timeout = 96 else: self.active_notes[note] = 1 timeout = 500 # key repeat delay listener.note_on(octave, note_index) sublime.set_timeout_async(lambda: self.stop_or_extend_note(note), timeout)
def run(self, edit, character='^'): """Available parameters: edit (sublime.Edit) The edit parameter from TextCommand. character (str) = '^' The character to insert when suggesting where the test assertions should go. """ view = self.view view.replace(edit, view.sel()[0], character) insert_at = view.sel()[0].begin() _, col = view.rowcol(insert_at) listener = sublime_plugin.find_view_event_listener( view, SyntaxTestHighlighterListener) if not listener.header: return lines, line = listener.get_details_of_line_being_tested() if not lines[-1].assertion_colrange: return end_token = listener.header.comment_end # don't duplicate the end token if it is on the line but not selected if end_token and view.sel()[0].end() == lines[0].line_region.end(): end_token = ' ' + end_token else: end_token = '' if character == '-': length = 1 scopes = { view.scope_name(line.begin() + lines[0].assertion_colrange[0]) } elif character == '^': length, scopes = self.determine_test_extends(lines, line, col) else: return suggest_suffix = get_setting('syntax_test.suggest_scope_suffix', True) scope = find_common_scopes(scopes, not suggest_suffix) # delete the existing selection if not view.sel()[0].empty(): view.erase(edit, view.sel()[0]) view.insert(edit, insert_at, (character * max(1, length)) + ' ' + scope + end_token) # move the selection to cover the added scope name, # so that the user can easily insert another ^ to extend the test view.sel().clear() view.sel().add( sublime.Region(insert_at + length, insert_at + length + len(' ' + scope + end_token)))
def on_post_text_command(self, view, command_name, args): if command_name == 'hide_auto_complete': listener = sublime_plugin.find_view_event_listener(view, SettingsListener) if listener: listener.is_completing_key = False elif command_name in ('commit_completion', 'insert_best_completion'): listener = sublime_plugin.find_view_event_listener(view, SettingsListener) if not (listener and listener.is_completing_key): return listener.is_completing_key = False sel = view.sel() if len(sel) != 1: # unclear what to do, so just do nothing return point = sel[0].begin() key_region = get_last_key_region(view, point) if key_region: key = view.substr(key_region) logger.debug("showing popup after inserting key completion for %r", key) listener.show_popup_for(key_region)
def on_post_text_command(self, view, command_name, args): if command_name == 'hide_auto_complete': listener = sublime_plugin.find_view_event_listener(view, SettingsListener) if listener: listener.is_completing_key = False elif command_name in ('commit_completion', 'insert_best_completion'): listener = sublime_plugin.find_view_event_listener(view, SettingsListener) if not (listener and listener.is_completing_key): return listener.is_completing_key = False sel = view.sel() if len(sel) != 1: # unclear what to do, so just do nothing return point = sel[0].begin() key_region = get_last_key_region(view, point) if key_region: key = view.substr(key_region) l.debug("showing popup after inserting key completion for %r", key) listener.show_popup_for(key_region)
def run(self, edit): self.view.run_command("commit_completion") # Don't add duplicated dot, if scope is edited in the middle. if self.view.substr(self.view.sel()[0].a) == ".": return # Check if the completed value was the base suffix # and don't re-open auto complete in that case. listener = sublime_plugin.find_view_event_listener(self.view, SyntaxDefCompletionsListener) if listener and listener.base_suffix: point = self.view.sel()[0].a region = sublime.Region(point - len(listener.base_suffix) - 1, point) if self.view.substr(region) == "." + listener.base_suffix: return # Insert a . and trigger next completion self.view.run_command('insert', {'characters': "."}) self.view.run_command('auto_complete', {'disable_auto_insert': True})
def run(self, edit): self.view.run_command("commit_completion") # Don't add duplicated dot, if scope is edited in the middle. if self.view.substr(self.view.sel()[0].a) == ".": return # Check if the completed value was the base suffix # and don't re-open auto complete in that case. listener = sublime_plugin.find_view_event_listener(self.view, SyntaxDefCompletionsListener) if listener and listener.base_suffix: point = self.view.sel()[0].a region = sublime.Region(point - len(listener.base_suffix) - 1, point) if self.view.substr(region) == "." + listener.base_suffix: return # Insert a . and trigger next completion self.view.run_command('insert', {'characters': "."}) self.view.run_command('auto_complete', {'disable_auto_insert': True})
def run(self, edit): view = self.view listener = sublime_plugin.find_view_event_listener( view, SyntaxTestHighlighterListener) if not listener.header: return suggest_suffix = get_setting('syntax_test.suggest_scope_suffix', True) for region in reversed(view.sel()): line = view.line(region.b) forest = ScopeTreeNode.build_forest( view.extract_tokens_with_scopes(line), trim_suffix=not suggest_suffix, ) tests = self.get_test_lines(forest, listener.header, line.begin()) view.insert(edit, line.end(), ''.join(tests))
def run(self, edit): listener = sublime_plugin.find_view_event_listener(self.view, PianoTune) # take the notes from the selection or entire buffer regions = self.view.sel() if len(regions) == 1 and regions[0].empty(): regions = [sublime.Region(0, self.view.size())] # TODO: when playing a selection, should it automatically find the octave and tempo, from before the selection, or expect the user to select those too? # TODO: think about how left hand vs right hand vs both hand playing could work # - should they be in separate files, or marked up in a single file? # - maybe easier to understand the files if separate, especially with labels etc. # TODO: a mode where only the keys light up without the sound playing # - and a mode where it waits for the user to press the key before continuing on # - here the left and right hand (i.e. if user is playing right hand and left is on auto-play) need to stay synced up tokens = piano_tunes.parse_piano_tune(piano_tunes.get_tokens_from_regions(self.view, regions)) #print(list(tokens)) states = piano_tunes.resolve_piano_tune_instructions(tokens) midi_messages = piano_tunes.convert_piano_tune_to_midi(states) #from pprint import pprint #midi_messages = list(midi_messages); pprint(midi_messages) listener.play_midi_instructions(midi_messages)
def run(self, edit, character='^'): """Available parameters: edit (sublime.Edit) The edit parameter from TextCommand. character (str) = '^' The character to insert when suggesting where the test assertions should go. """ view = self.view view.replace(edit, view.sel()[0], character) insert_at = view.sel()[0].begin() listener = sublime_plugin.find_view_event_listener(view, SyntaxTestHighlighterListener) if not listener.header: return lines, line = listener.get_details_of_line_being_tested() end_token = listener.header.comment_end # don't duplicate the end token if it is on the line but not selected if end_token and view.sel()[0].end() == lines[0].line_region.end(): end_token = ' ' + end_token else: end_token = '' scopes = [] length = 0 # find the following columns on the line to be tested where the scopes don't change test_at_start_of_comment = False col = view.rowcol(insert_at)[1] assertion_colrange = lines[0].assertion_colrange or (-1, -1) if assertion_colrange[0] == assertion_colrange[1]: col = assertion_colrange[1] test_at_start_of_comment = True lines = lines[1:] col_start, col_end = lines[0].assertion_colrange base_scope = path.commonprefix([ view.scope_name(pos) for pos in range(line.begin() + col_start, line.begin() + col_end) ]) for pos in range(line.begin() + col, line.end() + 1): scope = view.scope_name(pos) if len(scopes) == 0: scopes.append(scope) elif not scope.startswith(base_scope): break length += 1 if test_at_start_of_comment: break # find the unique scopes at each existing assertion position if lines and not test_at_start_of_comment: col_start, col_end = lines[0].assertion_colrange for pos in range(line.begin() + col_start, line.begin() + col_end): scope = view.scope_name(pos) if scope not in scopes: scopes.append(scope) suggest_suffix = get_setting('syntax_test.suggest_scope_suffix', True) scope = find_common_scopes(scopes, not suggest_suffix) # delete the existing selection if not view.sel()[0].empty(): view.erase(edit, view.sel()[0]) view.insert(edit, insert_at, (character * max(1, length)) + ' ' + scope + end_token) # move the selection to cover the added scope name, # so that the user can easily insert another ^ to extend the test view.sel().clear() view.sel().add(sublime.Region( insert_at + length, insert_at + length + len(' ' + scope + end_token) ))
def is_enabled(self): listener = sublime_plugin.find_view_event_listener( self.view, SyntaxTestHighlighterListener, ) return bool(listener and listener.header)
def on_post_save(self, view): listener = sublime_plugin.find_view_event_listener(view, SettingsListener) if listener and listener.known_settings: listener.known_settings.trigger_settings_reload()
def play_note(self, parse_state): note_index = parse_state.instruction.value listener = sublime_plugin.find_view_event_listener(self.view, PianoTune) listener.play_note_with_duration(parse_state.current_octave, note_index, parse_state.duration)
def find_piano(self): piano = get_piano_view() if piano: return sublime_plugin.find_view_event_listener(piano, Piano)
def is_enabled(self): listener = sublime_plugin.find_view_event_listener(self.view, PianoTune) return listener is not None
def run(self, edit): listener = sublime_plugin.find_view_event_listener(self.view, PianoTune) listener.playback_stopped = True
def is_enabled(self): listener = sublime_plugin.find_view_event_listener(self.view, PianoTune) return listener is not None and not listener.playback_stopped
def run(self, edit, character='^'): """Available parameters: edit (sublime.Edit) The edit parameter from TextCommand. character (str) = '^' The character to insert when suggesting where the test assertions should go. """ view = self.view view.replace(edit, view.sel()[0], character) insert_at = view.sel()[0].begin() _, col = view.rowcol(insert_at) listener = sublime_plugin.find_view_event_listener(view, SyntaxTestHighlighterListener) if not listener.header: return lines, line = listener.get_details_of_line_being_tested() if not lines[-1].assertion_colrange: return end_token = listener.header.comment_end # don't duplicate the end token if it is on the line but not selected if end_token and view.sel()[0].end() == lines[0].line_region.end(): end_token = ' ' + end_token else: end_token = '' if character == '-': length = 1 scopes = {view.scope_name(line.begin() + lines[0].assertion_colrange[0])} elif character == '^': length, scopes = self.determine_test_extends(lines, line, col) else: return suggest_suffix = get_setting('syntax_test.suggest_scope_suffix', True) scope = find_common_scopes(scopes, not suggest_suffix) trim_prefix = not get_setting('syntax_test.suggest_asserted_prefix', False) if trim_prefix: scopes_above = [ self.view.substr(sublime.Region( line.line_region.begin() + line.assertion_colrange[1], line.line_region.end(), )).strip() for line in lines[1:] if line.assertion_colrange[0] <= lines[0].assertion_colrange[0] and line.assertion_colrange[1] >= lines[0].assertion_colrange[1] ] for scope_above in reversed(scopes_above): # Determine the last scope segment matched by any previous assertion # and trim that and everything preceding it. score = sublime.score_selector(scope, scope_above) if score > 0: scope_parts = scope.split(' ') matched_count = -(-score.bit_length() // 3) - 1 score_of_last_part = score >> matched_count * 3 possible_score_of_last_part = len(scope_parts[matched_count - 1].split('.')) if score_of_last_part != possible_score_of_last_part: matched_count -= 1 scope = ' '.join(scope_parts[matched_count:]) # delete the existing selection if not view.sel()[0].empty(): view.erase(edit, view.sel()[0]) view.insert(edit, insert_at, (character * max(1, length)) + ' ' + scope + end_token) # move the selection to cover the added scope name, # so that the user can easily insert another ^ to extend the test view.sel().clear() view.sel().add(sublime.Region( insert_at + length, insert_at + length + len(' ' + scope + end_token) ))
def stop_or_extend_note(self, note): self.active_notes[note] -= 1 if self.active_notes[note] <= 0: del self.active_notes[note] listener = sublime_plugin.find_view_event_listener(self.view, Piano) listener.note_off(*note)
def on_post_save(self, view): listener = sublime_plugin.find_view_event_listener( view, SettingsListener) if listener and listener.known_settings: listener.known_settings.trigger_settings_reload()
def is_enabled(self): return sublime_plugin.find_view_event_listener(self.view, Piano) is not None