Repository: ploomber/jupysql-plugin (language: python)

Issue:
Only autocomplete when cell starts with `%%sql`



Discussion / hints:
And `%sql`. Do it by default so users don't have to click.

Fix patch:
diff --git a/src/completer/customconnector.ts b/src/completer/customconnector.ts
index 8631525..32953b8 100644
--- a/src/completer/customconnector.ts
+++ b/src/completer/customconnector.ts
@@ -12,6 +12,9 @@ import {
 
 import { keywords } from './keywords.json';
 
+const CELL_MAGIC = '%%sql';
+const LINE_MAGIC = '%sql';
+
 /**
  * A custom connector for completion handlers.
  */
@@ -31,7 +34,19 @@ export class SQLCompleterProvider implements ICompletionProvider {
    * @param context - additional information about context of completion request
    */
   async isApplicable(context: ICompletionContext): Promise<boolean> {
-    return true;
+    const editor = context.editor;
+    if (editor === undefined)
+      return false;
+
+    // If this is a SQL magic cell, then we can complete
+    const firstLine = editor.getLine(0);
+    if (firstLine.slice(0, CELL_MAGIC.length) === CELL_MAGIC)
+      return true;
+
+    // Otherwise, if we're to the right of a line magic, we can complete
+    const currPos = editor.getCursorPosition();
+    const lineMagicPos = editor.getLine(currPos.line).indexOf(LINE_MAGIC);
+    return (lineMagicPos > -1 && lineMagicPos + LINE_MAGIC.length < currPos.column);
   }
 
   /**


Test patch:
diff --git a/ui-tests/tests/completer.test.ts b/ui-tests/tests/completer.test.ts
index 7050500..17223ad 100644
--- a/ui-tests/tests/completer.test.ts
+++ b/ui-tests/tests/completer.test.ts
@@ -14,17 +14,17 @@ async function createNotebook(page: IJupyterLabPageFixture) {
 
 const samples = {
   'upper case': {
-    input: 'SEL',
+    input: '%sql SEL',
     expected: ['SELECT', 'SELECT DISTINCT', 'SELECT INTO', 'SELECT TOP'],
     unexpected: ['INSERT']
   },
   'lower case': {
-    input: 'sel',
+    input: '%sql sel',
     expected: ['SELECT', 'SELECT DISTINCT', 'SELECT INTO', 'SELECT TOP'],
     unexpected: ['INSERT']
   },
   'in-word': {
-    input: 'se',
+    input: '%sql se',
     expected: ['SELECT', 'SELECT DISTINCT', 'SELECT INTO', 'SELECT TOP', 'INSERT INTO'],
     unexpected: []
   }
@@ -56,7 +56,7 @@ test('test complete updates cell', async ({ page }) => {
   await createNotebook(page);
 
   await page.notebook.enterCellEditingMode(0);
-  await page.keyboard.type('SEL');
+  await page.keyboard.type('%sql SEL');
 
   await page.keyboard.press('Tab');
 
@@ -70,3 +70,58 @@ test('test complete updates cell', async ({ page }) => {
   });
 
 });
+
+const contexts = {
+  'valid cell magic': {
+    input: '%%sql\nSEL',
+    completion: true
+  },
+  'invalid cell magic': {
+    input: ' %%sql\nSEL',
+    completion: false
+  },
+  'line magic': {
+    input: '%sql SEL',
+    completion: true
+  },
+  'line magic with python': {
+    input: 'result = %sql SEL',
+    completion: true
+  },
+  'line magic new line': {
+    input: '%sql SEL\nSEL',
+    completion: false
+  },
+  'no magic': {
+    input: 'SEL',
+    completion: false
+  }
+}
+
+for (const [ name, { input, completion} ] of Object.entries(contexts))
+  test(`test ${name} ${completion ? 'does' : 'does not'} complete`, async ({ page }) => {
+    await createNotebook(page);
+
+    await page.notebook.enterCellEditingMode(0);
+    await page.keyboard.type(input);
+
+    await page.keyboard.press('Tab');
+    const suggestions = page.locator('.jp-Completer');
+    if (completion)
+      await expect(suggestions).toBeVisible();
+    else
+      await expect(suggestions).not.toBeVisible();
+  });
+
+test('test no completion before line magic', async ({ page }) => {
+  await createNotebook(page);
+
+  await page.notebook.enterCellEditingMode(0);
+  await page.keyboard.type('SEL = %sql SEL');
+  for (let i=0; i<11; i++)
+    await page.keyboard.press('ArrowLeft');
+
+  await page.keyboard.press('Tab');
+  const suggestions = page.locator('.jp-Completer');
+  await expect(suggestions).not.toBeVisible();
+});