diff --git a/ivette/package.json b/ivette/package.json
index bedf12a40ddbf6d65752f15353aec760bfbebc31..665132667063b1add37e44d44a1309a4b11c2ab2 100644
--- a/ivette/package.json
+++ b/ivette/package.json
@@ -60,6 +60,7 @@
     "codemirror": "^5.65.2",
     "@codemirror/view": "6.2.3",
     "@codemirror/state": "6.1.1",
+    "@codemirror/search": "6.2.3",
     "@codemirror/language": "6.2.1",
     "@codemirror/commands": "6.1.0",
     "@codemirror/lang-cpp": "6.0.1",
diff --git a/ivette/src/dome/renderer/text/editor.tsx b/ivette/src/dome/renderer/text/editor.tsx
index 6f0438512f5f3702932e122c44c7cbe4dcea7a48..3426e8b4fa6e2961bf229bf8a7e01b09d2471a25 100644
--- a/ivette/src/dome/renderer/text/editor.tsx
+++ b/ivette/src/dome/renderer/text/editor.tsx
@@ -31,7 +31,8 @@ import { Decoration, DecorationSet } from '@codemirror/view';
 import { DOMEventMap as EventMap } from '@codemirror/view';
 import { GutterMarker, gutter } from '@codemirror/view';
 import { Tooltip, showTooltip } from '@codemirror/view';
-import { lineNumbers } from '@codemirror/view';
+import { lineNumbers, keymap } from '@codemirror/view';
+import { searchKeymap, search, openSearchPanel } from '@codemirror/search';
 
 import { parser } from '@lezer/cpp';
 import { tags } from '@lezer/highlight';
@@ -328,6 +329,12 @@ export const LanguageHighlighter: Extension =
 //  Standard extensions and commands
 // -----------------------------------------------------------------------------
 
+export const ReadOnly = EditorState.readOnly.of(true);
+
+const SearchAlternativeKey = [{ key: 'Alt-f', run: openSearchPanel }];
+const SearchKeymap = searchKeymap.slice(1).concat(SearchAlternativeKey);
+export const Search : Extension = [ search(), keymap.of(SearchKeymap) ];
+
 export const Selection = createSelectionField();
 function createSelectionField(): Field<EditorSelection> {
   const cursor = EditorSelection.cursor(0);
diff --git a/ivette/src/dome/renderer/text/style.css b/ivette/src/dome/renderer/text/style.css
index d936197776803018537a588dc925c0d07239a09e..e339d8c9e86655ef19237ec5e918018db1ca0ed4 100644
--- a/ivette/src/dome/renderer/text/style.css
+++ b/ivette/src/dome/renderer/text/style.css
@@ -124,7 +124,7 @@
 
 .cm-editor .cm-gutters {
   border-right: 0px;
-  width: 2.15em;
+  min-width: 2.15em;
   background: var(--background-report);
 }
 
@@ -132,6 +132,52 @@
   color: var(--disabled-text);
 }
 
+.cm-editor .cm-panels {
+  font-size: 13px;
+  background: var(--background-report);
+  color: var(--text);
+}
+
+.cm-editor .cm-panels-bottom {
+  border-top: 2px solid var(--background-softer);
+}
+
+.cm-editor .cm-panel.cm-search {
+  padding: 4px 6px 4px 6px;
+  display: flex;
+  align-items: center;
+  gap: 2px;
+}
+
+.cm-editor .cm-panel.cm-search label {
+  font-size: 80%;
+  display: flex;
+  align-items: center;
+}
+
+.cm-editor .cm-panel.cm-search [name=close] {
+  color: var(--info-text);
+}
+
+.cm-editor .cm-textfield {
+  background: var(--background-report);
+  color: var(--text);
+  border: 1px solid var(--border);
+}
+
+.cm-editor .cm-button {
+  background-image: var(--default-button-img);
+  border: 1px solid var(--border);
+  color: var(--text);
+}
+
+.cm-editor .cm-panel.cm-gotoLine {
+  padding: 4px 6px 4px 6px;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
 .cm-property-gutter {
   width: 1.3em;
   background: var(--code-bullet);
diff --git a/ivette/src/frama-c/kernel/ASTview.tsx b/ivette/src/frama-c/kernel/ASTview.tsx
index 63495a564ee613daa4f03a3c70cbcd06e717b926..c923ee9d4d176bf13075d479b7bd1e26b99afbf5 100644
--- a/ivette/src/frama-c/kernel/ASTview.tsx
+++ b/ivette/src/frama-c/kernel/ASTview.tsx
@@ -617,6 +617,7 @@ const extensions: Editor.Extension[] = [
   PropertiesGutter,
   TaintedLvaluesDecorator,
   TaintTooltip,
+  Editor.ReadOnly,
   Editor.FoldGutter,
   Editor.LanguageHighlighter,
 ];
diff --git a/ivette/src/frama-c/kernel/SourceCode.tsx b/ivette/src/frama-c/kernel/SourceCode.tsx
index cc059c919a4224523c4849c5cf03e76aa0a9e008..2ede47f2e646753847e6a99bec84d8ec08f326aa 100644
--- a/ivette/src/frama-c/kernel/SourceCode.tsx
+++ b/ivette/src/frama-c/kernel/SourceCode.tsx
@@ -25,11 +25,12 @@ import * as Path from 'path';
 
 import * as Dome from 'dome';
 import * as System from 'dome/system';
-import * as Boxes from 'dome/layout/boxes';
 import * as Editor from 'dome/text/editor';
 import * as Labels from 'dome/controls/labels';
 import * as Settings from 'dome/data/settings';
 import * as Buttons from 'dome/controls/buttons';
+import * as Dialogs from 'dome/dialogs';
+import * as Toolbars from 'dome/frame/toolbars';
 
 import { Selection, Document } from 'dome/text/editor';
 
@@ -232,6 +233,8 @@ function useSourceGetter(): GetSource {
 // Necessary extensions.
 const extensions: Editor.Extension[] = [
   Source,
+  Editor.Search,
+  Editor.ReadOnly,
   Editor.Selection,
   Editor.LineNumbers,
   Editor.LanguageHighlighter,
@@ -263,9 +266,22 @@ export default function SourceCode(): JSX.Element {
   React.useEffect(() => Locations.set(view, { ivette: loc }), [view, loc]);
 
   const externalEditorTitle =
-    'Open the source file in an external editor.\nA Ctrl-click '
-    + 'in the source code opens the editor at the selected location.'
-    + '\nThe editor used can be configured in Ivette settings.';
+    'Open the source file in an external editor.';
+
+  const shortcuts =
+    'Ctrl+click: open file in an external editor at the selected location.\n'
+    + 'Alt+f: search for text or regexp.\n'
+    + 'Alt+g: go to line.';
+
+  const shortcutsTitle = 'Useful shortcuts:\n' + shortcuts;
+
+  async function displayShortcuts(): Promise<void> {
+    await Dialogs.showMessageBox({
+      buttons: [{label:"Ok"}],
+      details: shortcuts,
+      message: 'Useful shortcuts'
+    });
+  }
 
   return (
     <>
@@ -277,7 +293,13 @@ export default function SourceCode(): JSX.Element {
           title={externalEditorTitle}
         />
         <Labels.Code title={file}>{filename}</Labels.Code>
-        <Boxes.Hfill />
+        <Toolbars.Filler />
+        <Buttons.IconButton
+          icon="HELP"
+          onClick={displayShortcuts}
+          title={shortcutsTitle}
+        />
+        <Toolbars.Inset/>
       </Ivette.TitleBar>
       <Component style={{ fontSize: `${fontSize}px` }} />
     </>
diff --git a/ivette/yarn.lock b/ivette/yarn.lock
index aa98dbadf52cd4824088c375fd06493f2cb58f29..f7180e42c20f5b24bf92b7b5217dde6ff84cea2a 100644
--- a/ivette/yarn.lock
+++ b/ivette/yarn.lock
@@ -1816,6 +1816,15 @@
     "@lezer/lr" "^1.0.0"
     style-mod "^4.0.0"
 
+"@codemirror/search@6.2.3":
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-6.2.3.tgz#fab933fef1b1de8ef40cda275c73d9ac7a1ff40f"
+  integrity sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==
+  dependencies:
+    "@codemirror/state" "^6.0.0"
+    "@codemirror/view" "^6.0.0"
+    crelt "^1.0.5"
+
 "@codemirror/state@6.1.1", "@codemirror/state@^6.0.0":
   version "6.1.1"
   resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-6.1.1.tgz#4f512e5e34ea23a5e10b2c1fe43f6195e90417bb"
@@ -3840,6 +3849,11 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+crelt@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
+  integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
+
 crocket@^0.9.11:
   version "0.9.11"
   resolved "https://registry.yarnpkg.com/crocket/-/crocket-0.9.11.tgz#288fca11ef0d3dd239b62c488265f30c8edfb0c5"