Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Tatuya Kamada
gitlab-ce
Commits
9e4a4098
Commit
9e4a4098
authored
Sep 09, 2014
by
Dmitriy Zaporozhets
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #7646 from bushong1/snippet-search3
Adding in snippet search functionality
parents
002ce69e
4561a09c
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
392 additions
and
6 deletions
+392
-6
app/controllers/search_controller.rb
app/controllers/search_controller.rb
+7
-0
app/helpers/application_helper.rb
app/helpers/application_helper.rb
+2
-0
app/models/snippet.rb
app/models/snippet.rb
+14
-0
app/services/search/snippet_service.rb
app/services/search/snippet_service.rb
+14
-0
app/views/layouts/_search.html.haml
app/views/layouts/_search.html.haml
+2
-0
app/views/search/_results.html.haml
app/views/search/_results.html.haml
+7
-4
app/views/search/_snippet_filter.html.haml
app/views/search/_snippet_filter.html.haml
+13
-0
app/views/search/results/_snippet_blob.html.haml
app/views/search/results/_snippet_blob.html.haml
+65
-0
app/views/search/results/_snippet_title.html.haml
app/views/search/results/_snippet_title.html.haml
+23
-0
app/views/search/show.html.haml
app/views/search/show.html.haml
+4
-2
features/snippet_search.feature
features/snippet_search.feature
+20
-0
features/steps/shared/search.rb
features/steps/shared/search.rb
+11
-0
features/steps/shared/snippet.rb
features/steps/shared/snippet.rb
+23
-0
features/steps/snippet_search.rb
features/steps/snippet_search.rb
+56
-0
lib/gitlab/snippet_search_results.rb
lib/gitlab/snippet_search_results.rb
+131
-0
No files found.
app/controllers/search_controller.rb
View file @
9e4a4098
...
...
@@ -5,6 +5,7 @@ class SearchController < ApplicationController
@project
=
Project
.
find_by
(
id:
params
[
:project_id
])
if
params
[
:project_id
].
present?
@group
=
Group
.
find_by
(
id:
params
[
:group_id
])
if
params
[
:group_id
].
present?
@scope
=
params
[
:scope
]
@show_snippets
=
params
[
:snippets
].
eql?
'true'
@search_results
=
if
@project
return
access_denied!
unless
can?
(
current_user
,
:download_code
,
@project
)
...
...
@@ -14,6 +15,12 @@ class SearchController < ApplicationController
end
Search
::
ProjectService
.
new
(
@project
,
current_user
,
params
).
execute
elsif
@show_snippets
unless
%w(snippet_blobs snippet_titles)
.
include?
(
@scope
)
@scope
=
'snippet_blobs'
end
Search
::
SnippetService
.
new
(
current_user
,
params
).
execute
else
unless
%w(projects issues merge_requests)
.
include?
(
@scope
)
@scope
=
'projects'
...
...
app/helpers/application_helper.rb
View file @
9e4a4098
...
...
@@ -178,6 +178,8 @@ module ApplicationHelper
def
search_placeholder
if
@project
&&
@project
.
persisted?
"Search in this project"
elsif
@snippet
||
@snippets
||
@show_snippets
'Search snippets'
elsif
@group
&&
@group
.
persisted?
"Search in this group"
else
...
...
app/models/snippet.rb
View file @
9e4a4098
...
...
@@ -65,4 +65,18 @@ class Snippet < ActiveRecord::Base
def
expired?
expires_at
&&
expires_at
<
Time
.
current
end
class
<<
self
def
search
(
query
)
where
(
'(title LIKE :query OR file_name LIKE :query)'
,
query:
"%
#{
query
}
%"
)
end
def
search_code
(
query
)
where
(
'(content LIKE :query)'
,
query:
"%
#{
query
}
%"
)
end
def
accessible_to
(
user
)
where
(
'private = ? OR author_id = ?'
,
false
,
user
)
end
end
end
app/services/search/snippet_service.rb
0 → 100644
View file @
9e4a4098
module
Search
class
SnippetService
attr_accessor
:current_user
,
:params
def
initialize
(
user
,
params
)
@current_user
,
@params
=
user
,
params
.
dup
end
def
execute
snippet_ids
=
Snippet
.
accessible_to
(
current_user
).
pluck
(
:id
)
Gitlab
::
SnippetSearchResults
.
new
(
snippet_ids
,
params
[
:search
])
end
end
end
app/views/layouts/_search.html.haml
View file @
9e4a4098
...
...
@@ -5,6 +5,8 @@
-
if
@project
&&
@project
.
persisted?
=
hidden_field_tag
:project_id
,
@project
.
id
=
hidden_field_tag
:search_code
,
true
-
if
@snippet
||
@snippets
=
hidden_field_tag
:snippets
,
true
=
hidden_field_tag
:repository_ref
,
@ref
=
submit_tag
'Go'
if
ENV
[
'RAILS_ENV'
]
==
'test'
.search-autocomplete-opts.hide
{
:'data-autocomplete-path'
=>
search_autocomplete_path
,
:'data-autocomplete-project-id'
=>
@project
.
try
(
:id
),
:'data-autocomplete-project-ref'
=>
@ref
}
...
...
app/views/search/_results.html.haml
View file @
9e4a4098
%h4
#{
@search_results
.
total_count
}
results found
-
if
@project
for
#{
link_to
@project
.
name_with_namespace
,
@project
}
-
elsif
@group
for
#{
link_to
@group
.
name
,
@group
}
-
unless
@show_snippets
-
if
@project
for
#{
link_to
@project
.
name_with_namespace
,
@project
}
-
elsif
@group
for
#{
link_to
@group
.
name
,
@group
}
%hr
...
...
@@ -11,6 +12,8 @@
.col-sm-3
-
if
@project
=
render
"project_filter"
-
elsif
@show_snippets
=
render
'snippet_filter'
-
else
=
render
"global_filter"
.col-sm-9
...
...
app/views/search/_snippet_filter.html.haml
0 → 100644
View file @
9e4a4098
%ul
.nav.nav-pills.nav-stacked.search-filter
%li
{
class:
(
"active"
if
@scope
==
'snippet_blobs'
)}
=
link_to
search_filter_path
(
scope:
'snippet_blobs'
,
snippets:
true
,
group_id:
nil
,
project_id:
nil
)
do
%i
.icon-code
Snippet Contents
.pull-right
=
@search_results
.
snippet_blobs_count
%li
{
class:
(
"active"
if
@scope
==
'snippet_titles'
)}
=
link_to
search_filter_path
(
scope:
'snippet_titles'
,
snippets:
true
,
group_id:
nil
,
project_id:
nil
)
do
%i
.icon-book
Titles and Filenames
.pull-right
=
@search_results
.
snippet_titles_count
app/views/search/results/_snippet_blob.html.haml
0 → 100644
View file @
9e4a4098
.search-result-row
%span
=
snippet_blob
[
:snippet_object
].
title
by
=
link_to
user_snippets_path
(
snippet_blob
[
:snippet_object
].
author
)
do
=
image_tag
avatar_icon
(
snippet_blob
[
:snippet_object
].
author_email
),
class:
"avatar avatar-inline s16"
,
alt:
''
=
snippet_blob
[
:snippet_object
].
author_name
%span
.light
#{
time_ago_with_tooltip
(
snippet_blob
[
:snippet_object
].
created_at
)
}
%h4
.snippet-title
-
snippet_path
=
reliable_snippet_path
(
snippet_blob
[
:snippet_object
])
=
link_to
snippet_path
do
.file-holder
.file-title
%i
.icon-file
%strong
=
snippet_blob
[
:snippet_object
].
file_name
%span
.options
.btn-group.tree-btn-group.pull-right
-
if
snippet_blob
[
:snippet_object
].
author
==
current_user
=
link_to
"Edit"
,
edit_snippet_path
(
snippet_blob
[
:snippet_object
]),
class:
"btn btn-tiny"
,
title:
'Edit Snippet'
=
link_to
"Delete"
,
snippet_path
(
snippet_blob
[
:snippet_object
]),
method: :delete
,
data:
{
confirm:
"Are you sure?"
},
class:
"btn btn-tiny"
,
title:
'Delete Snippet'
=
link_to
"Raw"
,
raw_snippet_path
(
snippet_blob
[
:snippet_object
]),
class:
"btn btn-tiny"
,
target:
"_blank"
-
if
gitlab_markdown?
(
snippet_blob
[
:snippet_object
].
file_name
)
.file-content.wiki
-
snippet_blob
[
:snippet_chunks
].
each
do
|
snippet
|
-
unless
snippet
[
:data
].
empty?
=
preserve
do
=
markdown
(
snippet
[
:data
])
-
else
.file-content.code
.nothing-here-block
Empty file
-
elsif
markup?
(
snippet_blob
[
:snippet_object
].
file_name
)
.file-content.wiki
-
snippet_blob
[
:snippet_chunks
].
each
do
|
snippet
|
-
unless
snippet
[
:data
].
empty?
=
render_markup
(
snippet_blob
[
:snippet_object
].
file_name
,
snippet
[
:data
])
-
else
.file-content.code
.nothing-here-block
Empty file
-
else
.file-content.code
%div
.highlighted-data
{
class:
user_color_scheme_class
}
.line-numbers
-
snippet_blob
[
:snippet_chunks
].
each
do
|
snippet
|
-
unless
snippet
[
:data
].
empty?
-
snippet
[
:data
].
lines
.
to_a
.
size
.
times
do
|
index
|
-
offset
=
defined?
(
snippet
[
:start_line
])
?
snippet
[
:start_line
]
:
1
-
i
=
index
+
offset
=
link_to
snippet_path
+
"#L
#{
i
}
"
,
id:
"L
#{
i
}
"
,
rel:
"#L
#{
i
}
"
do
%i
.icon-link
=
i
-
unless
snippet
==
snippet_blob
[
:snippet_chunks
].
last
%a
=
"."
.highlight.term
%pre
%code
-
snippet_blob
[
:snippet_chunks
].
each
do
|
snippet
|
-
unless
snippet
[
:data
].
empty?
=
snippet
[
:data
]
-
unless
snippet
==
snippet_blob
[
:snippet_chunks
].
last
%a
=
"..."
-
else
.file-content.code
.nothing-here-block
Empty file
app/views/search/results/_snippet_title.html.haml
0 → 100644
View file @
9e4a4098
.search-result-row
%h4
.snippet-title.term
=
link_to
reliable_snippet_path
(
snippet_title
)
do
=
truncate
(
snippet_title
.
title
,
length:
60
)
-
if
snippet_title
.
private?
%span
.label.label-gray
%i
.icon-lock
private
%span
.cgray.monospace.tiny.pull-right.term
=
snippet_title
.
file_name
%small
.pull-right.cgray
-
if
snippet_title
.
project_id?
=
link_to
snippet_title
.
project
.
name_with_namespace
,
project_path
(
snippet_title
.
project
)
.snippet-info
=
"#
#{
snippet_title
.
id
}
"
%span
by
=
link_to
user_snippets_path
(
snippet_title
.
author
)
do
=
image_tag
avatar_icon
(
snippet_title
.
author_email
),
class:
"avatar avatar-inline s16"
,
alt:
''
=
snippet_title
.
author_name
%span
.light
#{
time_ago_with_tooltip
(
snippet_title
.
created_at
)
}
app/views/search/show.html.haml
View file @
9e4a4098
...
...
@@ -9,10 +9,12 @@
=
submit_tag
'Search'
,
class:
"btn btn-create"
.form-group
.col-sm-2
.col-sm-10
=
render
'filter'
,
f:
f
-
unless
params
[
:snippets
].
eql?
'true'
.col-sm-10
=
render
'filter'
,
f:
f
=
hidden_field_tag
:project_id
,
params
[
:project_id
]
=
hidden_field_tag
:group_id
,
params
[
:group_id
]
=
hidden_field_tag
:snippets
,
params
[
:snippets
]
=
hidden_field_tag
:scope
,
params
[
:scope
]
.results.prepend-top-10
...
...
features/snippet_search.feature
0 → 100644
View file @
9e4a4098
@dashboard
Feature
:
Snippet Search
Background
:
Given
I sign in as a user
And
I have public
"Personal snippet one"
snippet
And
I have private
"Personal snippet private"
snippet
And
I have a public many lined snippet
Scenario
:
I
should see my public and private snippets
When
I search for
"snippet"
in snippet titles
Then
I should see
"Personal snippet one"
in results
And
I should see
"Personal snippet private"
in results
Scenario
:
I
should see three surrounding lines on either side of a matching snippet line
When
I search for
"line seven"
in snippet contents
Then
I should see
"line four"
in results
And
I should see
"line seven"
in results
And
I should see
"line ten"
in results
And
I should not see
"line three"
in results
And
I should not see
"line eleven"
in results
features/steps/shared/search.rb
0 → 100644
View file @
9e4a4098
module
SharedSearch
include
Spinach
::
DSL
def
search_snippet_contents
(
query
)
visit
"/search?search=
#{
URI
::
encode
(
query
)
}
&snippets=true&scope=snippet_blobs"
end
def
search_snippet_titles
(
query
)
visit
"/search?search=
#{
URI
::
encode
(
query
)
}
&snippets=true&scope=snippet_titles"
end
end
features/steps/shared/snippet.rb
View file @
9e4a4098
...
...
@@ -18,4 +18,27 @@ module SharedSnippet
private:
true
,
author:
current_user
)
end
And
'I have a public many lined snippet'
do
create
(
:personal_snippet
,
title:
'Many lined snippet'
,
content:
<<-
END
.
gsub
(
/^\s+\|/
,
''
),
|line one
|line two
|line three
|line four
|line five
|line six
|line seven
|line eight
|line nine
|line ten
|line eleven
|line twelve
|line thirteen
|line fourteen
END
file_name:
'many_lined_snippet.rb'
,
private:
true
,
author:
current_user
)
end
end
features/steps/snippet_search.rb
0 → 100644
View file @
9e4a4098
class
Spinach::Features::SnippetSearch
<
Spinach
::
FeatureSteps
include
SharedAuthentication
include
SharedPaths
include
SharedSnippet
include
SharedUser
include
SharedSearch
step
'I search for "snippet" in snippet titles'
do
search_snippet_titles
'snippet'
end
step
'I search for "snippet private" in snippet titles'
do
search_snippet_titles
'snippet private'
end
step
'I search for "line seven" in snippet contents'
do
search_snippet_contents
'line seven'
end
step
'I should see "line seven" in results'
do
page
.
should
have_content
'line seven'
end
step
'I should see "line four" in results'
do
page
.
should
have_content
'line four'
end
step
'I should see "line ten" in results'
do
page
.
should
have_content
'line ten'
end
step
'I should not see "line eleven" in results'
do
page
.
should_not
have_content
'line eleven'
end
step
'I should not see "line three" in results'
do
page
.
should_not
have_content
'line three'
end
Then
'I should see "Personal snippet one" in results'
do
page
.
should
have_content
'Personal snippet one'
end
And
'I should see "Personal snippet private" in results'
do
page
.
should
have_content
'Personal snippet private'
end
Then
'I should not see "Personal snippet one" in results'
do
page
.
should_not
have_content
'Personal snippet one'
end
And
'I should not see "Personal snippet private" in results'
do
page
.
should_not
have_content
'Personal snippet private'
end
end
lib/gitlab/snippet_search_results.rb
0 → 100644
View file @
9e4a4098
module
Gitlab
class
SnippetSearchResults
<
SearchResults
attr_reader
:limit_snippet_ids
def
initialize
(
limit_snippet_ids
,
query
)
@limit_snippet_ids
=
limit_snippet_ids
@query
=
query
end
def
objects
(
scope
,
page
=
nil
)
case
scope
when
'snippet_titles'
Kaminari
.
paginate_array
(
snippet_titles
).
page
(
page
).
per
(
per_page
)
when
'snippet_blobs'
Kaminari
.
paginate_array
(
snippet_blobs
).
page
(
page
).
per
(
per_page
)
else
super
end
end
def
total_count
@total_count
||=
snippet_titles_count
+
snippet_blobs_count
end
def
snippet_titles_count
@snippet_titles_count
||=
snippet_titles
.
count
end
def
snippet_blobs_count
@snippet_blobs_count
||=
snippet_blobs
.
count
end
private
def
snippet_titles
Snippet
.
where
(
id:
limit_snippet_ids
).
search
(
query
).
order
(
'updated_at DESC'
)
end
def
snippet_blobs
search
=
Snippet
.
where
(
id:
limit_snippet_ids
).
search_code
(
query
)
search
=
search
.
order
(
'updated_at DESC'
).
to_a
snippets
=
[]
search
.
each
{
|
e
|
snippets
<<
chunk_snippet
(
e
)
}
snippets
end
def
default_scope
'snippet_blobs'
end
# Get an array of line numbers surrounding a matching
# line, bounded by min/max.
#
# @returns Array of line numbers
def
bounded_line_numbers
(
line
,
min
,
max
)
lower
=
line
-
surrounding_lines
>
min
?
line
-
surrounding_lines
:
min
upper
=
line
+
surrounding_lines
<
max
?
line
+
surrounding_lines
:
max
(
lower
..
upper
).
to_a
end
# Returns a sorted set of lines to be included in a snippet preview.
# This ensures matching adjacent lines do not display duplicated
# surrounding code.
#
# @returns Array, unique and sorted.
def
matching_lines
(
lined_content
)
used_lines
=
[]
lined_content
.
each_with_index
do
|
line
,
line_number
|
used_lines
.
concat
bounded_line_numbers
(
line_number
,
0
,
lined_content
.
size
)
if
line
.
include?
(
query
)
end
used_lines
.
uniq
.
sort
end
# 'Chunkify' entire snippet. Splits the snippet data into matching lines +
# surrounding_lines() worth of unmatching lines.
#
# @returns a hash with {snippet_object, snippet_chunks:{data,start_line}}
def
chunk_snippet
(
snippet
)
lined_content
=
snippet
.
content
.
split
(
"
\n
"
)
used_lines
=
matching_lines
(
lined_content
)
snippet_chunk
=
[]
snippet_chunks
=
[]
snippet_start_line
=
0
last_line
=
-
1
# Go through each used line, and add consecutive lines as a single chunk
# to the snippet chunk array.
used_lines
.
each
do
|
line_number
|
if
last_line
<
0
# Start a new chunk.
snippet_start_line
=
line_number
snippet_chunk
<<
lined_content
[
line_number
]
elsif
last_line
==
line_number
-
1
# Consecutive line, continue chunk.
snippet_chunk
<<
lined_content
[
line_number
]
else
# Non-consecutive line, add chunk to chunk array.
snippet_chunks
<<
{
data:
snippet_chunk
.
join
(
"
\n
"
),
start_line:
snippet_start_line
+
1
}
# Start a new chunk.
snippet_chunk
=
[
lined_content
[
line_number
]]
snippet_start_line
=
line_number
end
last_line
=
line_number
end
# Add final chunk to chunk array
snippet_chunks
<<
{
data:
snippet_chunk
.
join
(
"
\n
"
),
start_line:
snippet_start_line
+
1
}
# Return snippet with chunk array
{
snippet_object:
snippet
,
snippet_chunks:
snippet_chunks
}
end
# Defines how many unmatching lines should be
# included around the matching lines in a snippet
def
surrounding_lines
3
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment