Source editing is one of the central parts of GPS, giving in turn access to many other functionalities, including extended source navigation and source analyzing tools.
The integrated source editor provides all the usual capabilities found in integrated environments, including:
If the file is maintained under version control, and version control is supported and enabled in GPS, the first box on the left will show VCS information on the file: the VCS kind (e.g. CVS), followed by the revision number, and if available, the status of the file.
The second box shows the current editing mode. This is either Insert or Overwrite and can be changed using the insert keyboard keys by default.
The third box shows the writable state of the file. You can change this state by clicking on the label directly: this will switch between Writable and Read Only. Note that this will not change the permissions of the file on disk, it will only change the writable state of the source editor within GPS.
When trying to save a file which is read only on the disk, GPS will ask for confirmation, and if possible, will force saving of the file, keeping its read only state.
The fourth box shows whether the file has been modified since the last save. The three possible states are:
The file has been saved and not modified since.
The fifth box displays the position of the cursor in the file by a line and a column number.
Based on the programming language associated with the file, reserved words and languages constructs such as comments and strings are highlighted in different colors and fonts. See The Preferences Dialog for a list of settings that can be customized.
By default, GPS knows about many languages. You can also easily add support for other languages through XML files. Most languages supported by GPS will provide syntax highlighting in the editor.
When enabled, lines are automatically indented each time you press the Enter key, or by pressing the indentation key. The indentation key is Ctrl-Tab by default, and can be changed in the key manager dialog, The Key Manager Dialog.
If a set of lines is selected when you press the indentation key, this whole set of lines will be indented.
When you leave the mouse over a word in the source editor, a small window will automatically pop up if there are relevant contextual information to display about the word.
The type of information displayed depends on the current state of GPS.
In normal mode, the entity kind and the location of declaration is displayed when this information is available. That is, when the cross-reference information about the current file has been generated. If there is no relevant information, no tooltip is displayed. See Support for Cross-References for more information.
In addition, the documentation for the entity is displayed. This is the block of comments just before or just after the entity’s declaration of body. There mustn’t be any blank line between the two. For instance, the following are valid documentation for Ada and C:
-- A comment for A
A : Integer;
B : Integer;
-- A comment for B
C : Integer;
-- Not a comment for C, there is a blank linke
In debugging mode, the value of the variable under the mouse is displayed in the pop up window if the variable is known to the debugger. Otherwise, the normal mode information is displayed.
You can disable the automatic pop up of tool tips in the Editor section of the preferences dialog. The Preferences Dialog.
GPS provides two kinds of code completion: a smart code completion based on semantic information, and a text completion.
It is useful when editing a file and using often the same words to get automatic word completion. This is possible by typing the Ctrl-/ key combination (customizable through the key manager dialog) after a partial word: the next possible completion will be inserted in the editor. Typing this key again will cycle through the list of possible completions.
Text completions are searched in all currently open source files, by first looking at the closest words and then looking further in the source as needed.
When the cursor is moved before an opening delimiter or after a closing delimiter, then both delimiters will be highlighted. The following characters are considered delimiters: ()[]{}. You can disable highlighting of delimiters in the preferences.
You can also jump to a corresponding delimiter by using the Ctrl-' key, that can be configured in the preferences. Typing twice on this key will move the cursor back to its original position.
You can configure the editor to highlight the current line with a certain color. The Preferences Dialog.
If this preference is enabled, the editor will highlight the current block of code, e.g. the current begin...end block, or loop statement, etc...
The block highlighting will also take into account the changes made in your source code, and will recompute automatically the current block when needed.
This capability is currently implemented for Ada, C and C++ languages.
When enabled, the editor will display - icons on the left side, corresponding to the beginning of subprograms. If you click on one of these icons, all the lines corresponding to this subprogram are hidden, except the first one. As for the block highlighting, these icons are recomputed automatically when you modify your sources and are always kept up to date.
This capability is currently implemented for Ada, C and C++ languages.
You can configure the editor to periodically save modified files. See Autosave delay for a full description of this capability.
When the cursor is positioned on an entity in the source editor, GPS will highlight all references to this entity in the current editor.
When the cursor moves away from the entity, the highlighting is removed.
This is controlled by the plugin auto_highlight_occurrences.py: it can be deactivated by deactivating the plugin (The Plug-ins Editor).
Details such as presence of indications in the Speed Column or highlighting color can be customized in the Plugins section of The Preferences Dialog.
GPS also integrates with existing third party editors such as Emacs or vi. Using an External Editor.
In addition to the standard keys used to navigate in the editor (up, down, right, left, page up, page down), the integrated editor provides a number of key bindings allowing easy navigation in the file.
There are also several ways to define new key bindings, see Defining text aliases and Binding actions to keys.
Pressing these three keys and then holding Ctrl-Shift allow you to enter characters using their hexadecimal value. For example, pressing Ctrl-Shift-u-2-0 will insert a space character (ASCII 32, which is 20 in hexadecimal).
The file selector is a dialog used to select a file. Under Windows, the default is to use the standard file selection widget. Under other platforms, the file selector is a built-in dialog:
This dialog provides the following areas and capabilities:
Rectangle commands operate on a rectangular area of the text, that is all the characters between two columns in a certain range of lines.
A rectangle is selected using the standard selection mechanism. You can therefore use either the mouse to highlight the proper region, or shift and the cursor keys to extend the selection, or the Emacs selection (with the mark and the current cursor location) if you have activated the emacs.py plugin.
Visually, a selected rectangle is exactly the same as the standard selection. In particular, the characters after the last column, on each line, will also be highlighted. The way the selection is interpreted (either as a full text or as a rectangle) depends on the command you then chose to manipulate the selection.
If you chose one of the commands from the /Edit/Rectangles menu, the actual rectangle will extend from the top-left corner down to the bottom-right corner. All characters to the right of the right-most column, although they are highlighted, are not part of the rectangle.
Consider for instance the following initial text:
package A is
procedure P;
procedure Q;
end A;
and assume we have selected from the character “p” in “procedure P”, down to the character “c” in “procedure Q”.
The following commands can then be used (either from the menu, or you can assign key shortcuts to them via the usual /Edit/Key shortcuts menu.
Cut or Delete These commands will remove the selected text (and have no effect on empty lines within the rectangle). The former will in addition copy the rectangle to the clipboard, so that you can paste it later. In our example, we end up with:
package A is
edure P;
edure Q;
end A;
Copy This command has no visual effect, but copies the contents of the rectangle into the clipboard.
Paste Pastes the contents of the clipboard as a rectangle: each line from the clipboard is treated independently, and inserted on successive lines in the current editor. They all start in the same column (the one where the cursor is initially in), and existing text in the editor lines is shifted to the right). If for instance you now place the cursor in the second line, first column, and paste, we end up with:
package A is
proc edure P;
proc edure Q;
end A;
Clear Replaces the contents of the selected rectangle with spaces. If we start from our initial exmaple, we end up with the following. Note the difference with Delete:
package A is
edure P;
edure Q;
end A;
Open Replaces the contents of the selected rectangle with spaces, but shifts the lines to the right to do so. Note the difference with Clear:
package A is
procedure P;
procedure Q;
end A;
Replace With Text This is similar to Clear, but the rectangle is replaced with user-defined text. The lines will be shifted left or right if the text you insert is shorter (resp. longer) than the width of the rectangle. If for instance we replace our initial rectangle with the text TMP, we end up with the following. Note that the character “c” has disappeared, since TMP is shorter than our rectangle width (4 characters). This command will impact lines that are empty in the initial rectangle:
package A is
TMPedure P;
TMP
TMPedure Q;
end A;
Insert Text This inserts a text to the left of the rectangle on each line. The following example inserts TMP. Note the difference with Replace With Text. This command will also insert the text on lines that are empty in the initial rectangle:
package A is
TMPprocedure P;
TMP
TMPprocedure Q;
end A;
Sort This sorts the selected lines according to the key which starts and ends on the corresponding rectangle’s columns:
aaa 15 aa
bbb 02 bb
ccc 09 cc
With a selection starting from the 1 on the first line and ending on the 9 on the last one, sorting will result with the following content:
bbb 02 bb
ccc 09 cc
aaa 15 aa
Sort reverse
As above but in the reverse order.
It is often convenient to be able to repeat a given key sequence a number of times.
GPS supports this with several different methods:
Repeat the next action
If there is a single key press that you wish to repeat a number of times, you should first use the GPS action “Repeat Next” (bound by default to control-u, but this can be changed as usual through the /Edit/Key Shortcuts menu), then entering the number of times you wish to repeat, and finally pressing the key you want.
For instance, the following sequence control-u 79 - will insert 79 characters ‘-‘ in the current editor. This proves often useful to insert separators.
If you are using the emacs mode (see /Tools/Plug-ins menu), you can also use the sequence control-u 30 control-k to delete 30 lines.
Recording macros
If you wish to repeat a sequence of more than 1 key, you should record this sequence as a macro. All macro-related menus are found in /Tools/Macros, although it is often more convenient to use these through key bindings, which you can of course override.
You must indicate to GPS that it should start recording the keys you are pressing. This is done through the /Tools/Macros/Start Keyboard Macro menu. As its name indicates, this only records keyboard events, not mouse events. Until you select /Tools/Macros/Stop Macro, GPS will keep recording the events.
In Emacs mode, the macro actions are bound to control-x (, control-x ) and control-x e key shortcuts. For instance, you can execute the following to create a very simple macro that deletes the current line, wherever your cursor initially is on that line:
GPS keeps a set of case exceptions that is used by all case insensitive languages. When editing or reformatting a buffer for such a language the case exception dictionary will be checked first. If an exception is found for this word or a substring of the word, it will be used; otherwise the specified casing for keywords or identifiers is used. A substring is defined as a part of the word separated by underscores.
Note that this feature is not activated for entities (keywords or identifiers) for which the casing is set to Unchanged. See The Preferences Dialog.
A contextual menu named Casing has the following entries:
To add or remove a substring exception into/from the dictionary you need to first select the substring on the editor. In this case the last two contextual menu entries will be:
GPS includes basic facilities for refactoring your code. Refactoring is the standard term used to describe manipulation of the source code that do not affect the behavior of the application, but help reorganize the source code to make it more readable, more extendable, ...
Refactoring technics are generally things that programmers are used to do by hand, but which are faster and more secure to do automatically through a tool.
One of the basic recommendations when you refactor your code is to recompile and test your application very regularly, to make sure that each of the small modifications you made to it didn’t break the behavior of your application. This is particularly true with GPS, since it relies on the cross-references information that is generated by the compiler. If some of the source files have not been recompiled recently, GPS will print warning messages indicating that the renaming operation might be dangerous and/or only partial.
One of the reference books that was used in the choice of refactoring methods to implement is “Refactoring”, by Martin Fowler (Addison Wesley).
Clicking on an entity in a source file and selecting the Refactoring/Rename menu will open a dialog asking for the new name of the entity. GPS will rename all instances of the entity in your application. This includes the definition of the entity, its body, all calls to it, etc... Of course, no comment is updated, and you should probably check manually that the comment for the entity still applies.
GPS will handle primitive operations by also renaming the operations it overrides or that overrides it. This means that any dispatching call to that operation will also be renamed, and the application should still work as before. If you are renaming a parameter to a subprogram, GPS can also rename parameters with similar names in overriding or overridden subprograms.
The behavior when handling read-only files can be specified: by default, GPS will not do any refactoring in these files, and will display a dialog listing all of them; but you can also choose to make them writable just as if you had clicked on the “Read-Only” button in the status bar of the editor and then have GPS perform the renaming in them as well.
If you are editing Ada code and click on a call to a subprogram, GPS will display a contextual menu Refactoring/Name parameters, which will replace all unnamed parameters by named parameters, as in:
Call (1, 2)
=>
Call (Param1 => 1, Param2 => 2);
This refactoring is used to move some code from one place to a separate subprogram. The goal is to simplify the original subprogram, by moving part of its code elsewhere.
Here is an example from the “Refactoring” book. The refactoring will take place in the body of the package pkg.adb, but the spec is needed so that you can compile the source code (a preliminary step mandatory before you can refactor the code):
pragma Ada_05;
with Ada.Containers.Indefinite_Doubly_Linked_Lists;
with Ada.Strings.Unbounded;
package Pkg is
type Order is tagged null record;
function Get_Amount (Self : Order) return Integer;
package Order_Listsis new
Ada.Containers.Indefinite_Doubly_Linked_Lists (Order);
type Invoice is tagged record
Orders : Order_Lists.List;
Name : Ada.Strings.Unbounded.Unbounded_String;
end record;
procedure Print_Owing (Self : Invoice);
end Pkg;
The initial implementation for this code is given by the following code:
pragma Ada_05;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with Ada.Text_IO; use Ada.Text_IO;
package body Pkg is
use Order_Lists;
----------------
-- Get_Amount --
----------------
function Get_Amount (Self : Order) return Integer is
begin
return 0;
end Get_Amount;
-----------------
-- Print_Owing --
-----------------
procedure Print_Owing (Self : Invoice) is
E : Order_Lists.Cursor := First (Self.Orders);
Outstanding : Natural := 0;
Each : Order;
begin
-- <<< line 30
-- Print Banner
Put_Line ("");
Put_Line (" Customer Owes ");
Put_Line (""); -- << line 35
-- Calculate Outstanding
while Has_Element (E) loop
Each := Element (E);
Outstanding := Outstanding + Each.Get_Amount;
Next (E);
end loop;
-- Print Details
Put_Line ("Name: " & To_String (Self.Name));
Put_Line ("Outstanding:" & Outstanding'Img);
end Print_Owing;
end Pkg;
The procedure Print_Owing is too long and does several independent actions. We will perform a series of three successive refactoring steps to extract the code and move it elsewhere.
The first is the code that prints the banner. Moving it is easy, since this code does not depend on any context. We could just do a copy-paste, but then we would have to create the new subprogram. Instead, we select lines 30 to 35, and then select the contextual menu Refactoring/Extract Subprogram. GPS will then automatically change Print_Owing and create a new procedure Print_Banner (the name is specified by the user, GPS does not try to guess it). Also, since the chunk of code that is extracted starts with a comment, GPS automatically uses that comment as the documentation for the new subprogram. Here is part of the resulting file:
package body Pkg is
procedure Print_Banner;
-- Print Banner
------------------
-- Print_Banner --
------------------
procedure Print_Banner is
begin
Put_Line ("");
Put_Line (" Customer Owes ");
Put_Line ("");
end Print_Banner;
... (code not shown)
procedure Print_Owing (Self : Invoice) is
E : Order_Lists.Cursor := First (Self.Orders);
Outstanding : Natural := 0;
Each : Order;
begin
Print_Banner;
-- Calculate Outstanding
while Has_Element (E) loop
Each := Element (E);
Outstanding := Outstanding + Each.Get_Amount;
Next (E);
end loop;
-- Print Details <<< line 54
Put_Line ("Name: " & To_String (Self.Name));
Put_Line ("Outstanding:" & Outstanding'Img); -- line 57
end Print_Owing;
end Pkg;
A more interesting example is when we want to extract the code to print the details of the invoice. This code depends on one local variable and the parameter to Print_Owing. When we select lines 54 to 57 and extract it into a new Print_Details subprogram, we get the following result. GPS automatically decides which variables to extract, and whether they should become parameters of the new subprogram, or local variables. In the former case, it will also automatically decide whether to create “in”, “out” or “in out” parameters. If there is a single “out” parameter, it will automatically create a function rather than a procedure.
GPS will use, for the parameters, the same name that was used for the local variable. Very often, it will make sense to recompile the new version of the source, and then apply the Rename Entity refactoring to have more specific names for the parameters, or the Name Parameters refactoring so that the call to the new method uses named parameters to further clarify the code:
... code not shown
procedure Print_Details
(Self : Invoice'Class;
Outstanding : Natural);
-- Print Details
-------------------
-- Print_Details --
-------------------
procedure Print_Details
(Self : Invoice'Class;
Outstanding : Natural)
is
begin
Put_Line ("Name: " & To_String (Self.Name));
Put_Line ("Outstanding:" & Outstanding'Img);
end Print_Details;
procedure Print_Owing (Self : Invoice) is
E : Order_Lists.Cursor := First (Self.Orders);
Outstanding : Natural := 0;
Each : Order;
begin
Print_Banner;
-- Calculate Outstanding
while Has_Element (E) loop
Each := Element (E);
Outstanding := Outstanding + Each.Get_Amount;
Next (E);
end loop;
Print_Details (Self, Outstanding);
end Print_Owing;
Finally, we want to extract the code that computes the outstanding amount. When this code is moved, the variables E and Each become useless in Print_Owing and are moved into the new subprogram (which we will call Get_Outstanding. Here is the result of that last refactoring (the initial selection should include the blank lines before and after the code, to keep the resulting Print_Owing simpler). GPS will automatically ignore those blank lines:
... code not shown
procedure Get_Outstanding (Outstanding : in out Natural);
-- Calculate Outstanding
---------------------
-- Get_Outstanding --
---------------------
procedure Get_Outstanding (Outstanding : in out Natural) is
E : Order_Lists.Cursor := First (Self.Orders);
Each : Order;
begin
while Has_Element (E) loop
Each := Element (E);
Outstanding := Outstanding + Each.Get_Amount;
Next (E);
end loop;
end Get_Outstanding;
procedure Print_Owing (Self : Invoice) is
Outstanding : Natural := 0;
begin
Print_Banner;
Get_Outstanding (Outstanding);
Print_Details (Self, Outstanding);
end Print_Owing;
Note that the final version of Print_Owing is not perfect. For instance, passing the initial value 0 to Get_Outstanding is useless, and in fact that should probably be a function with no parameter. But GPS already saves a lot of time and manipulation.
Finally, a word of caution: this refactoring does not check that you are giving a valid input. For instance, if the text you select includes a declare block, you should always include the full block, not just a part of it (or select text between begin and end). Likewise, GPS does not expect you to select any part of the variable declarations, just the code.
GPS is integrated with a number of external editors, in particular Emacs and vi. The choice of the default external editor is done in the preferences. The Preferences Dialog.
The following values are recognized:
This is the recommended client. It is based on Emacs, but needs an extra package to be installed. This is the only client that provides a full integration in GPS, since any extended lisp command can be sent to the Emacs server.
By default, gnuclient will open a new Emacs frame for every file that is opened. You might want to add the following code to your .emacs file (create one if needed) so that the same Emacs frame is reused every time:
(setq gnuserv-frame (car (frame-list)))
See http://www.hpl.hp.com/personal/ange/gnuserv/home.html for more information.
This is a program that is always available if you have installed Emacs. As opposed to starting a new Emacs every time, it will reuse an existing Emacs session. It is then extremely fast to open a file.
This client will start a new Emacs session every time a file needs to be opened. You should use emacsclient instead, since it is much faster, and makes it easier to copy and paste between multiple files. Basically, the only reason to use this external editor is if your system doesn’t support emacsclient.
Vim is a vi-like editor that provides a number of enhancements, for instance syntax highlighting for all the languages supported by GPS. Selecting this external editor will start an xterm (or command window, depending on your system) with a running vim process editing the file.
Note that one limitation of this editor is that if GPS needs to open the same file a second time, it will open a new editor, instead of reusing the existing one.
To enable this capability, the xterm executable must be found in the PATH, and thus is not supported on Windows systems. Under Windows systems, you can use the custom editor instead.
This editor works exactly like vim, but uses the standard vi command instead of vim.
You can specify any external editor by choosing this item. The full command line used to call the editor can be specified in the preferences (see Custom Editor Command).
In the cases that require an Emacs server, GPS will try several solutions if no already running server was found. It will first try to spawn the glide environment distributed with GNAT. If not found in the PATH, it will then start a standard Emacs. The project file currently used in GPS will be set appropriately the first time Emacs is spawned. This means that if you load a new project in GPS, or modify the paths of the current project, you should kill any running Emacs, so that a new one is spawned by GPS with the appropriate project.
Alternatively, you can reload explicitly the project from Emacs itself by using the menu Project->Load
In the preferences, there are three settings that allow you to select the external editor (if left to an empty string, GPS will automatically select the first editor available on your system), to specify the custom editor command, in case you’ve selector this item, and whether this editor should always be used every time you double-click on a file, or whether you need to explicitly select the contextual menu to open the external editor.
This section concerns X-Window users who are used to cutting and pasting with the middle mouse button. In the GPS text editor, as in many recent X applications, the GPS clipboard is set by explicit cut/copy/paste actions, either through menu items or keyboard shortcuts, and the primary clipboard (i.e. the ‘middle button’ clipboard) is set by the current selection.
Therefore, copy/paste between GPS and other X applications using the primary clipboard will still work, provided that there is some text currently selected. The GPS clipboard, when set, will override the primary clipboard.
By default, GPS overrides the X mechanism. To prevent this, add the following line: OVERRIDE_MIDDLE_CLICK_PASTE = no to your traces.cfg file. Note, however, that the X mechanism pastes all attributes of text, including coloring and editability, which can be confusing.
See http://standards.freedesktop.org/clipboards-spec/clipboards-latest.txt for more information.
After you have finished modifying your files, you need to save them. The basic method to do that is to select the menu File->Save, which saves the currently selected file.
You can also use the menu File->Save As... if you want to save the file with another name, or in another directory.
If you have multiple files to save, another possibility is to use the menu File->Save More->All. This will open a dialog listing all the currently modified editors that need saving. You can then select individually which one should be saved, and click on Save to do the actual saving.
When calling external commands, such as compiling a file, if the Auto save preference is disabled, this same dialog is also used, to make sure that e.g. the compiler will take into account your local changes. If the preference is enabled, the saving is performed automatically.
You can conveniently select or unselect all the files at once by clicking on the title of the first column (labeled Select). This will toggle the selection status of the first line, and have the same status for all other editors.
If you press Cancel instead of Save, no saving will take place, and the action that displayed this dialog is also canceled. Such actions can be for instance starting a compilation command, a VCS operation, or quitting GPS with unsaved files.
GPS has a basic support for working with files on remote hosts. This includes a number of protocols, described below, which allow you to read a file from a remote host, edit it locally, and then save it transparently to the remote machine.
For now, the support for remote files is only available through the GPS shell window. You start editing a remote file by typing a line similar to:
Editor.edit protocol://user@machine/full/path
where “protocol” should be replaced by the name of the protocol you want to use, “user” is the login name you wish to use on the remote “machine”, and “/full/path” is the full path on the remote machine to access the file.
The user name is optional. If it is the same as on the local machine, you can omit the user name as well as the “@” sign.
Likewise, the machine name is optional, if you want to get a file from the local host. This can be used to access files belonging to another user. In this case, you need to specify the “@” sign, but do not insert a machine name right after it.
Remote files can also be used if you want to work with GPS, but the machine on which the files are found isn’t supported by GPS.
The following protocols are supported:
This protocol is based on the ssh command line tool, which must therefore be available in the path. It provides encrypted and secure connections to the remote host. Files are transfered in-line, that is the connection is established the first time you access the remote host, and kept open for all further access.
Although ssh can be setup not to require a password, GPS will automatically detect if a password is asked and open a dialog to query it.
The remote system must be a Unix-like system with support for standard Unix commands like test, echo, rm and ls.
In the sample shell command above, you would replace the word “protocol” with “ssh” to use this protocol.
This protocol behaves like ssh, except that the connections are not encrypted. However, this protocol is generally available on all Unix machines by default.
It has the same requirements that the ssh protocol. To use it, substitute the word “rsh” to “protocol” in the example above.
This protocol is based on the standard telnet protocol. It behaves much like the two protocols above, with an unencrypted connection.
To use it, substitute the word “telnet” to “protocol” in the example above.
This protocol is also based on one of the tools of the ssh suite. It provides encrypted connections, and uses a mixture of ssh and scp connections. Various commands like querying the time stamp of a file are executed through a permanent ssh connection, whereas files are downloaded and uploaded through a one-time scp command.
It basically has the same behavior as the ssh protocol, although it might be slightly slower since a new connection has to be established every time a file is fetched from, or written to the remote host. However, it might work better than ssh if the file contains 8 bit characters.
To use it, substitute the word “scp” to “protocol” in the example above.
Just like scp is based on ssh, this protocol is based on either rsh or ssh. It depends on the external tool rsync, and uses a mixture of a rsh/ssh connection for commands like querying the time stamp of a file, and one-time connections with rsync to transfer the files.
Rsync is specially optimized to transfer only the parts of a file that are different from the one already on the remote host. Therefore, it will generally provide the best performance when writing the file back to the remote host.
If you set up the environment variable RSYNC_RSH to ssh before starting gps, the connection will then be encrypted when transferring the files, and the connection will be performed using ssh instead of rsh.
To use this protocol, substitute the word “rsync” to “protocol” in the example above.
This protocol provides only limited capabilities, but can be used to retrieve or write a file back through an ftp connection, possibly even through an anonymous ftp connection.
To use this protocol, substitute the word “ftp” to “protocol” in the example above.