Skip to content
This repository was archived by the owner on Jan 8, 2025. It is now read-only.

Commit 50c39e2

Browse files
Fix: Don't treat the tab trigger as part of $TM_SELECTED_TEXT
1 parent a2bdbf2 commit 50c39e2

File tree

4 files changed

+34
-8
lines changed

4 files changed

+34
-8
lines changed

lib/snippet-expansion.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
const {CompositeDisposable, Range, Point} = require('atom')
22

33
module.exports = class SnippetExpansion {
4-
constructor (snippet, editor, cursor, snippets) {
4+
constructor (snippet, editor, cursor, snippets, {method} = {}) {
55
this.settingTabStop = false
66
this.isIgnoringBufferChanges = false
77
this.onUndoOrRedo = this.onUndoOrRedo.bind(this)
@@ -12,6 +12,11 @@ module.exports = class SnippetExpansion {
1212
this.subscriptions = new CompositeDisposable
1313
this.selections = [this.cursor.selection]
1414

15+
// Method refers to how the snippet was invoked; known values are `prefix`
16+
// or `command`. If neither is present, then snippet was inserted
17+
// programmatically.
18+
this.method = method
19+
1520
// Holds the `Insertion` instance corresponding to each tab stop marker. We
1621
// don't use the tab stop's own numbering here; we renumber them
1722
// consecutively starting at 0 in the order in which they should be
@@ -191,7 +196,8 @@ module.exports = class SnippetExpansion {
191196
let params = {
192197
editor: this.editor,
193198
cursor: this.cursor,
194-
selectionRange: this.cursor.selection.getBufferRange()
199+
selectionRange: this.cursor.selection.getBufferRange(),
200+
method: this.method
195201
}
196202

197203
for (const variable of this.snippet.variables) {

lib/snippets.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,7 @@ module.exports = {
760760
}
761761
},
762762

763+
// Expands a snippet invoked via command.
763764
expandSnippet (editor, snippet) {
764765
this.getStore(editor).observeHistory({
765766
undo: event => { this.onUndoOrRedo(editor, event, true) },
@@ -771,11 +772,13 @@ module.exports = {
771772
editor.transact(() => {
772773
const cursors = editor.getCursors()
773774
for (const cursor of cursors) {
774-
this.insert(snippet, editor, cursor)
775+
this.insert(snippet, editor, cursor, {method: 'command'})
775776
}
776777
})
777778
},
778779

780+
// Expands a snippet defined via tab trigger _if_ such a snippet can be found
781+
// for the current prefix and scope.
779782
expandSnippetsUnderCursors (editor) {
780783
const snippet = this.snippetToExpandUnderCursor(editor)
781784
if (!snippet) { return false }
@@ -789,10 +792,12 @@ module.exports = {
789792
editor.transact(() => {
790793
const cursors = editor.getCursors()
791794
for (const cursor of cursors) {
795+
// Select the prefix text so that it gets consumed when the snippet
796+
// expands.
792797
const cursorPosition = cursor.getBufferPosition()
793798
const startPoint = cursorPosition.translate([0, -snippet.prefix.length], [0, 0])
794799
cursor.selection.setBufferRange([startPoint, cursorPosition])
795-
this.insert(snippet, editor, cursor)
800+
this.insert(snippet, editor, cursor, {method: 'prefix'})
796801
}
797802
})
798803
return true
@@ -884,14 +889,14 @@ module.exports = {
884889
this.getStore(editor).makeCheckpoint()
885890
},
886891

887-
insert (snippet, editor, cursor) {
892+
insert (snippet, editor, cursor, {method = null} = {}) {
888893
if (editor == null) { editor = atom.workspace.getActiveTextEditor() }
889894
if (cursor == null) { cursor = editor.getLastCursor() }
890895
if (typeof snippet === 'string') {
891896
const bodyTree = this.getBodyParser().parse(snippet)
892897
snippet = new Snippet({id: this.snippetIdCounter++, name: '__anonymous', prefix: '', bodyTree, bodyText: snippet})
893898
}
894-
return new SnippetExpansion(snippet, editor, cursor, this)
899+
return new SnippetExpansion(snippet, editor, cursor, this, {method})
895900
},
896901

897902
getUnparsedSnippets () {

lib/variable.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ function makeDateResolver (dateParams) {
2424
const RESOLVERS = {
2525
// All the TM_-prefixed variables are part of the LSP specification for
2626
// snippets.
27-
'TM_SELECTED_TEXT' ({editor, selectionRange}) {
27+
'TM_SELECTED_TEXT' ({editor, selectionRange, method}) {
28+
// When a snippet is inserted via tab trigger, the trigger is
29+
// programmatically selected prior to snippet expansion so that it is
30+
// consumed when the snippet body is inserted. The trigger _should not_ be
31+
// treated as selected text. There is no way for $TM_SELECTED_TEXT to
32+
// contain anything when a snippet is invoked via tab trigger.
33+
if (method === 'prefix') return ''
34+
2835
if (!selectionRange || selectionRange.isEmpty()) return ''
2936
return editor.getTextInBufferRange(selectionRange)
3037
},

spec/snippets-spec.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,8 @@ foo\
15341534
".source.js": {
15351535
"Uses TM_SELECTED_TEXT": {
15361536
body: 'lorem ipsum $TM_SELECTED_TEXT dolor sit amet',
1537-
command: 'test-command-tm-selected-text'
1537+
command: 'test-command-tm-selected-text',
1538+
prefix: 'tmSelectedText'
15381539
},
15391540
"Uses CLIPBOARD": {
15401541
body: 'lorem ipsum $CLIPBOARD dolor sit amet',
@@ -1599,6 +1600,13 @@ foo\
15991600
expect(editor.getText()).toBe('lorem ipsum (selected text) dolor sit amet');
16001601
});
16011602

1603+
it("does not consider the tab trigger to be part of $TM_SELECTED_TEXT when a snippet is invoked via tab trigger", () => {
1604+
editor.insertText('tmSelectedText');
1605+
simulateTabKeyEvent();
1606+
1607+
expect(editor.getText()).toBe('lorem ipsum dolor sit amet');
1608+
});
1609+
16021610
it("interpolates line number variables correctly", () => {
16031611
editor.insertText('ln');
16041612
simulateTabKeyEvent();

0 commit comments

Comments
 (0)