uqbar-project/wollok-lsp-ide

No mostrar snippets en todas las situaciones

Closed this issue · 10 comments

Actualmente esta hardcodeado que siempre se muestren los snippets para crear metodos, clases, tests, etc.

return [
...contextCompletions,
...templates,
]

Hacer estos snippets un poco mas inteligentes

Sumado a esto, los templates habria que arreglarlos para que puedan ser completados con Tab

export const templates = [
{
label: 'class',
kind: CompletionItemKind.Class,
data: 1,
detail: WOLLOK_AUTOCOMPLETE,
insertText: 'class ClassName {\n}',
},
{
label: 'object',
kind: CompletionItemKind.Text,
data: 2,
detail: WOLLOK_AUTOCOMPLETE,
insertText: 'object objectName {\n}',
},
{
label: 'method (with effect)',
kind: CompletionItemKind.Method,
data: 3,
detail: WOLLOK_AUTOCOMPLETE,
insertText: 'method methodName() {\n}',
},
{
label: 'method (without effect)',
kind: CompletionItemKind.Method,
data: 4,
detail: WOLLOK_AUTOCOMPLETE,
insertText: 'method methodName() = value',
},
{
label: 'describe',
kind: CompletionItemKind.Event,
data: 5,
detail: WOLLOK_AUTOCOMPLETE,
insertText: `describe "a group of tests" {
test "something" {
assert.that(true)
}
}
`,
},
{
label: 'test',
kind: CompletionItemKind.Event,
data: 6,
detail: WOLLOK_AUTOCOMPLETE,
insertText: `test "something" {
assert.that(true)
}
`,
},

Con la siguiente sintaxis: https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax

Mmmm.... @ivojawer @PalumboN , parece que hay un tema con ésto:

  • una cosa son los snippets, que te permiten editar con tabs dentro del código
  • y otro es el mecanismo de completion

snippetVsAutocomplete

El snippet es cómodo para moverte pero: 1. no te permite configurar íconos (nunca le quisieron poner ese feature), 2. tampoco participa del onCompletion, aparece siempre. Fíjense acá cómo funciona en TS que es medio cabeza, te permite meter un for en cualquier parte del código:

snippets

Para mí hay dos alternativas:

  • tener snippets de código: es fácil de implementar y queda cheto que te podés mover con tabs para definirlo, aunque hay que tener pocos snippets porque si no te marea con muchas opciones que además al no poder filtrar te permite crear un describe dentro de un object, por ejemplo
  • tener completion más inteligentes, con iconitos y basados en el nodo en el que estás (onda: Package permitiría crear objects, classes, describe y tests, mientras que en un class podrías crear var, const, method, y en un test solo const, methods, asserts, o asserts booleans.) Se puede parametrizar agregando un input (como cuando creás una clase, te pregunta el nombre), pero eso escupe el código y después lo modificás a mano.

Las dos opciones son complementarias, los snippets se pueden configurar para que aparezcan o no (sería parte de la config del IDE). Qué les parece?

+1 a tener completions inteligentes (es una opinion super personal igual, nunca me gustaron los snippets 😅)

Lo unico que agrego es que las completions tambien permiten la chetada de los tabs, por ejemplo los metodos hoy funcionan asi:

Screen.Recording.2023-10-07.at.17.50.23.mov

Estuve jugando un poco y me parece que tengo una propuesta más cerrada:

  • lo que hoy está configurado mediante templates, lo pasé a snippets: defino object, class, mixin, describe, test, program, var, const, método con y sin efecto, y probé también ponerle un filter para mostrar en el video, pero creo que prefiero no agregar demasiados chiches porque se vuelve muy intrusivo a la hora de codear (imaginate tener any, find, sum, casi uno por mensaje). El snippet no se puede controlar, aparece si escribís, pero por lo general les alumnes que ya se acostumbran a entender la jerarquía de elementos de Wollok tienen un gran paso ganado
  • por otra parte, el autocomplete de referencias y mensajes me parece que es la papa cuando empezamos a escribir los bodies de los métodos, tests, programas. Eso se genera con el método completions que está en el linter, basado en el nodo padre.

La experiencia es muy buena, mejor en este caso que Eclipse:

autoCompletePiola

ahh, nos cruzamos @ivojawer , comparto en nunca haber usado snippets jajaja, pero entiendo que ya tener un var, const, class, object, por más que no sea tan acotado, es good enough para quien escribe.

Sii, para mi asi esta excelente y ademas coincide con el comportamiento en otros lenguajes.

nooooooo!!! @ivojawer me vuelvo loco!! Encontré cómo tener

  • snippets
  • y además que sean inteligentes

En lo que teníamos hasta ahora solo faltaba una configuración más:

const completeSingleton = (): CompletionItem[] => [
  {
    label: 'var attribute',
    kind: CompletionItemKind.Field,
    sortText: 'a',
    insertTextFormat: InsertTextFormat.Snippet,  // <== la papa
    insertText: 'var ${1:energia} = ${0:0}',
  },
  ...
]

no hace falta nada más...

autoCompleteQueQueremos

lo único que tiene es que tenés que activarlo con Ctrl + Space, pero me parece incluso mejor ese comportamiento para que no sea intrusivo

Bueno, toda mi alegría derivó en una sensación medio amarga porque por un lado el parser está considerando los espacios (ya abrí un ticket por eso), pero además, no siempre va a funcionar esta estrategia. Supongamos que escribís algo como

class Bird {
   m // te parás acá y activás el autocompletado
}

eso hace que el parser se rompa, con lo cual estarías parado en una posición que se asocia al Package. El Package no tiene métodos, así que no te muestra ningún autocompletado:

image

En definitiva, me parece que el approach inteligente es demasiado ambicioso, porque la mayoría de les pibes (y me incluyo) quiere autocompletar escribiendo (a mí me molesta mucho la cantidad de sugerencias que me hace). Con lo cual, yo volvería a tener algo menos inteligente pero que no dependa tanto del parser sobre todo porque el parser no puede hacer magia cuando el programa se está construyendo.

Bueno, hoy desculé un poquito el problema, que está acá:

export const completions = (
  params: CompletionParams,
  environment: Environment,
): CompletionItem[] => {
  const { position, textDocument, context } = params
  const selectionNode = cursorNode(environment, position, textDocument)

  if (context?.triggerCharacter === '.') {
    // ignore dot
    position.character -= 1
    return completeMessages(environment, findFirstStableNode(selectionNode))
  } else {
    // return completionsForNode(findFirstStableNode(selectionNode)) <== first stable node es el tema
    return completionsForNode(selectionNode)
  }
}

como hay un error de parseo, findFirstStableNode sube al package, pero la verdad es que no me suena una buena estrategia: si hay un error de parseo, o incluso si estás escribiendo el nombre de una variable, deberías mostrar el autocompletado del nodo donde estás parado. Si estás en un método y hay un error, subís a nivel clase/objeto/mixin y entonces podrías crear un método dentro de otro método. Si estás en una clase y hay un error, te sugiere crear un objeto dentro de una clase/objeto/mixin... se me está escapando la tortuga o no estoy viendo dónde nos podría ayudar. @ivojawer @PalumboN Recuerdan por qué fue eso? Buceando en el código me topé con la sorpresa de que había metido yo autocompletado (??? ni me acordaba), sumado a que en este commit 93e802adfb119d3c3f278da7d204844cc35cfd38 está el agregado.

Miren ahora, pude sacar los snippets generales y ahora cuando te autocompleta sí lo hace en base al nodo:

autoCompleteLindo

Solo falta que el parser maneje bien los espacios donde terminan las llaves.

Eso lo habia agregado yo y la verdad que no recuerdo por que, me parece que tenia que ver con los nodos que tenian un error de parseo pero no se a que problema llevaba eso. Igualmente el parseo cambió desde entonces, tal vez ya no tiene ningun motivo de ser.