Content Proposals

Ingo Wechsung edited this page Feb 7, 2015 · 11 revisions


From version 3.20.131 on, we have Content Proposals (aka code completion or code assist) available in the FregIDE.

This document describes the proposals available in detail and gives some tips and tricks.

Activation and Handling

A proposal list can be required by typing Ctrl+Space in the editor. A popup list with one or more proposals will appear. As you browse through the list with the cursor keys, additional information is shown, depending on the proposal selected. Most often you see the type and documentation of the selected item.

Once you decide in favor of a proposal, hit Enter. The popup closes and you find some text inserted near the text cursor. You can always undo the change with Ctrl+Z.

If you don't want any of the proposals, hit Esc. The list vanishes and you can continue typing.

General Information

It is important to understand that the quality of the proposals offered depends on

  1. the token(s) before the text cursor
  2. the state of the parser/compiler.

Generally, proposals work best the more information the compiler has collected. However, in the interest of speedy responsiveness and stable compilation that relies, in each pass, on certain assumptions guaranteed by error free predecessor passes, errors are diagnosed as early as possible and subsequent passes are not run.

For example, it makes no sense to do type inference if there are syntax errors.

To be sure, in an IDE, the compiler state (i.e. the number of successful passes) may vary with each character typed. To smooth this a bit, there is a mechanism to keep the information gathered in earlier parses. Yet, this also presupposes that there was at least one error free compiler run.

Here is the list of error categories in the order of detection by the compiler:

  1. Lexical errors, like missing termination of quoted constructs and block comments.
  2. Failed module imports.
  3. Syntax errors.
  4. Different number of patterns in equations for the same name.
  5. Name resolution errors, illegal names in record patterns and record constructors.
  6. Type class errors, kind errors and instance errors
  7. Type errors.

In the following sections, we will assume an error free state unless stated otherwise. We will give usage hints in the form

example ¦

where the broken vertical bar stands for the text cursor at the moment when you hit Ctrl+Space. Note that white space is significant. The usage hint above would tells us that we can invoke a certain member proposal if there is an identifier and (some) space before the cursor.

Templates and Proposals

Module Template

If the file is empty and you invoke content assist, a module template is offered. Accept this by hitting Enter and you get the following code:

--- This is an undocumented module
module _Proposed_ where

import Data.List

The content proposer will guess at a module name that makes sense, based on the file name and the source directories defined in the project.

The import is merely there to avoid a syntax error, since a module must at least have one definition.

If the file is not empty and you nevertheless get the module template, you probably have lexical errors that prevented the scanner from delivering a token list.

Import Proposal

import ¦
import fre¦
import frege.¦
import Da¦
import Data.L¦

The proposal is a list with possible module name parts to complete the import declaration. It does understand abbreviated names like Data.List. Without a restriction, it gives all names possible on that level (1st and 3rd example).

Member Proposals


These proposals work on a literal or an identifier immediately followed by a dot and optional by some more letters. It offers type member functions (such as field names) that may be applicable to the value before the dot, based on the values type.

It is sometimes a good idea to wait a second before entering the dot so as to give the type checker the opportunity to find out the type of the identifier. (This is not an issue with literals, of course.)

Remember, typing interrupts the compiler, and it might not have proceeded to type checking yet. And oftentimes, the typing of the dot will lead to a syntax error so that it gives up early.

If you know or guess the first letter(s), you can also enter them. This will narrow the result list accordingly, and it will not be a syntax error nor hinder type inference.

You can go left and right using the cursor keys while the proposal list is displayed, this will update the proposals accordingly, and the result is sometimes better, as the type checker meanwhile may have reached the function you're in.

It can happen that the type of the item is not known (yet), or that it simply does not have a type that allows member proposals. For example

bar x = x.¦

Here, a list of all known type class members is displayed instead, which makes sense in the situation. As usual, the list can be restricted by typing some prefix characters.

Namespace Proposals¦

They work like member proposals, only for fixed types, and also for name spaces. The first usage example would you let choose between group and groupBy if Data.List was imported. The second one displays all functions and constructors that live in the namespace of type Maybe.

Function Equation Proposal

foo :: Maybe a -> Bool -> String

This proposal looks at the type defined for a name (which must be the first token on its line) and derives a skeleton of function definitions. For the example above one would get:

foo Nothing true  = undefined    -- TODO: complete code
foo Nothing false  = undefined    -- TODO: complete code
foo (Just _) true  = undefined    -- TODO: complete code
foo (Just _) false  = undefined    -- TODO: complete code

Annotation Proposal

They told you that you should write type annotations, but you're too lazy? No problem! Once your function type checks, invoke the annotation proposal thus:

¦foo (a,b) = (b,a)

The IDE will make it look like:

foo ∷ (α,β) → (β,α)
foo (a,b) = (b,a)

Unfortunately, that doesn't work for definitions in infix form.

Case Expression Proposal


The first example proposes a case expression that scrutinizes a value of the given type, here Maybe. On acceptance of the proposal, the type name will be replaced by the case expression, and the cursor is between case and of, ready to type the expression to be scrutinized.

The second example works only when the type constructor of the type of var is known and proposes a case expression that scrutinizes var.

The third example works only when the type of fun is known and if it is a function with a result type whose type constructor is known. It proposes a case expression that scrutinizes the result of application of fun to the correct number of arguments and leaves the cursor ready for typing of the arguments.

Name Completion Proposal


This helps to complete the name one is just typing. It finds constructors, namespaces, types, functions or variables that start with the given prefix.

The first example will most probably offer Just and the second Prelude, while the third one will also look for local names (like function arguments, where/let or pattern bound names) defined nearby.

The local names offered may not be valid at the current position, the content assistant has no idea about scoping. Yet the heuristic is good enough to identify names you should have in memory anyway.

Multiple Proposals

The alert reader will have recognized that in situations like


there is more than one proposal possible. In such situations, the possible proposals will be concatenated.

In the example, one could get a case expression proposal, and name completion proposals.

Default Proposal

If none of the above rules fired, the default proposal pops up, which contains simply all items the compiler knows about. This is not terribly helpful, but may evoke some inspiration.

Your Proposal?

If you have an idea for a proposal, don't hesitate to open an enhancement issue.

Or, better yet, why not write your own!? The code is in frege/ide/, look for function proposeContent.