Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Clone this wiki locally
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
If you don't want any of the proposals, hit
Esc. The list vanishes and you can continue typing.
It is important to understand that the quality of the proposals offered depends on
- the token(s) before the text cursor
- 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:
- Lexical errors, like missing termination of quoted constructs and block comments.
- Failed module imports.
- Syntax errors.
- Different number of patterns in equations for the same name.
- Name resolution errors, illegal names in record patterns and record constructors.
- Type class errors, kind errors and instance errors
- Type errors.
In the following sections, we will assume an error free state unless stated otherwise. We will give usage hints in the form
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
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 ¦ 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).
name.¦ name.prefix¦ 42.¦ "foo".to¦
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.
They work like member proposals, only for fixed types, and also for name spaces. The first usage example would you let choose between
Data.List was imported. The second one displays all functions and constructors that live in the namespace of type
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
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
Maybe¦ var¦ fun¦
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
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
Jus¦ Prel¦ x¦
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.
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.
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.
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/Utilities.fr, look for function