2021-05-26 15:11:35 +05:30
# frozen_string_literal: true
module Onebox
module Mixins
module GitBlobOnebox
def self . included ( klass )
klass . include ( Onebox :: Engine )
klass . include ( Onebox :: LayoutSupport )
klass . matches_regexp ( klass . git_regexp )
klass . always_https
klass . include ( InstanceMethods )
end
EXPAND_AFTER = 0b001
EXPAND_BEFORE = 0b010
EXPAND_NONE = 0b0
DEFAULTS = {
EXPAND_ONE_LINER : EXPAND_AFTER | EXPAND_BEFORE , #set how to expand a one liner. user EXPAND_NONE to disable expand
LINES_BEFORE : 10 ,
LINES_AFTER : 10 ,
SHOW_LINE_NUMBER : true ,
MAX_LINES : 20 ,
2023-01-09 12:10:19 +00:00
MAX_CHARS : 5000 ,
2021-05-26 15:11:35 +05:30
}
module InstanceMethods
def initialize ( url , timeout = nil )
super url , timeout
# merge engine options from global Onebox.options interface
# self.options = Onebox.options["GithubBlobOnebox"] # self.class.name.split("::").last.to_s
# self.options = Onebox.options[self.class.name.split("::").last.to_s] #We can use this a more generic approach. extract the engine class name automatically
self . options = DEFAULTS
@selected_lines_array = nil
@selected_one_liner = 0
@model_file = nil
# Define constant after merging options set in Onebox.options
# We can define constant automatically.
options . each_pair do | constant_name , value |
constant_name_u = constant_name . to_s . upcase
if constant_name_u == constant_name . to_s
#define a constant if not already defined
unless self . class . const_defined? constant_name_u . to_sym
2023-01-09 12:10:19 +00:00
Onebox :: Mixins :: GitBlobOnebox . const_set constant_name_u . to_sym ,
options [ constant_name_u . to_sym ]
2021-05-26 15:11:35 +05:30
end
end
end
end
private
def calc_range ( m , contents_lines_size )
truncated = false
2023-01-09 12:10:19 +00:00
from = / \ d+ / . match ( m [ :from ] ) #get numeric should only match a positive interger
to = / \ d+ / . match ( m [ :to ] ) #get numeric should only match a positive interger
2021-05-26 15:11:35 +05:30
range_provided = ! ( from . nil? && to . nil? ) #true if "from" or "to" provided in URL
2023-01-09 12:10:19 +00:00
from = from . nil? ? 1 : from [ 0 ] . to_i #if from not provided default to 1st line
to = to . nil? ? - 1 : to [ 0 ] . to_i #if to not provided default to undefiend to be handled later in the logic
2021-05-26 15:11:35 +05:30
2023-01-09 12:10:19 +00:00
if to === - 1 && range_provided #case "from" exists but no valid "to". aka ONE_LINER
2021-05-26 15:11:35 +05:30
one_liner = true
to = from
else
one_liner = false
end
2023-01-09 12:10:19 +00:00
unless range_provided #case no range provided default to 1..MAX_LINES
2021-05-26 15:11:35 +05:30
from = 1
2023-01-09 12:10:19 +00:00
to = MAX_LINES
2021-05-26 15:11:35 +05:30
truncated = true if contents_lines_size > MAX_LINES
#we can technically return here
end
2023-01-09 12:10:19 +00:00
from , to = [ from , to ] . sort #enforce valid range. [from < to]
from = 1 if from > contents_lines_size #if "from" out of TOP bound set to 1st line
to = contents_lines_size if to > contents_lines_size #if "to" is out of TOP bound set to last line.
2021-05-26 15:11:35 +05:30
if one_liner
@selected_one_liner = from
if EXPAND_ONE_LINER != EXPAND_NONE
if ( EXPAND_ONE_LINER & EXPAND_BEFORE != 0 ) # check if EXPAND_BEFORE flag is on
2023-01-09 12:10:19 +00:00
from = [ 1 , from - LINES_BEFORE ] . max # make sure expand before does not go out of bound
2021-05-26 15:11:35 +05:30
end
2023-01-09 12:10:19 +00:00
if ( EXPAND_ONE_LINER & EXPAND_AFTER != 0 ) # check if EXPAND_FLAG flag is on
2021-05-26 15:11:35 +05:30
to = [ to + LINES_AFTER , contents_lines_size ] . min # make sure expand after does not go out of bound
end
2023-01-09 12:10:19 +00:00
from = contents_lines_size if from > contents_lines_size #if "from" is out of the content top bound
2021-05-26 15:11:35 +05:30
# to = contents_lines_size if to > contents_lines_size #if "to" is out of the content top bound
else
#no expand show the one liner solely
end
end
2023-01-09 12:10:19 +00:00
if to - from > MAX_LINES && ! one_liner #if exceed the MAX_LINES limit correct unless range was produced by one_liner which it expand setting will allow exceeding the line limit
2021-05-26 15:11:35 +05:30
truncated = true
2023-01-09 12:10:19 +00:00
to = from + MAX_LINES - 1
2021-05-26 15:11:35 +05:30
end
{
2023-01-09 12:10:19 +00:00
from : from , #calculated from
from_minus_one : from - 1 , #used for getting currect ol>li numbering with css used in template
to : to , #calculated to
one_liner : one_liner , #boolean if a one-liner
selected_one_liner : @selected_one_liner , #if a one liner is provided we create a reference for it.
range_provided : range_provided , #boolean if range provided
truncated : truncated ,
2021-05-26 15:11:35 +05:30
}
end
#minimize/compact leading indentation while preserving overall indentation
def removeLeadingIndentation ( str )
min_space = 100
a_lines = str . lines
a_lines . each do | l |
2023-01-09 12:10:19 +00:00
l = l . chomp ( " \n " ) # remove new line
2023-01-20 12:52:49 -06:00
m = l . match ( / \ A[ ]* / ) # find leading spaces 0 or more
2023-02-16 10:40:11 +01:00
if m . nil? || l . size == m [ 0 ] . size || l . size == 0
next # SKIP no match or line is only spaces
else # no match | only spaces in line | empty line
2021-05-26 15:11:35 +05:30
m_str_length = m [ 0 ] . size
2023-01-09 12:10:19 +00:00
if m_str_length < = 1 # minimum space is 1 or nothing we can break we found our minimum
2021-05-26 15:11:35 +05:30
min_space = m_str_length
break #stop iteration
end
2023-01-09 12:10:19 +00:00
min_space = m_str_length if m_str_length < min_space
2021-05-26 15:11:35 +05:30
end
end
a_lines . each do | l |
2023-01-09 12:10:19 +00:00
re = Regexp . new " ^[ ]{ #{ min_space } } " #match the minimum spaces of the line
2021-05-26 15:11:35 +05:30
l . gsub! ( re , " " )
end
a_lines . join
end
def line_number_helper ( lines , start , selected )
lines = removeLeadingIndentation ( lines . join ) . lines # A little ineffeicent we could modify removeLeadingIndentation to accept array and return array, but for now it is only working with a string
hash_builder = [ ]
output_builder = [ ]
2023-01-09 12:10:19 +00:00
lines . map . with_index do | line , i |
2021-05-26 15:11:35 +05:30
lnum = ( i . to_i + start )
2023-01-09 12:10:19 +00:00
hash_builder . push (
line_number : lnum ,
data : line . gsub ( " \n " , " " ) ,
selected : ( selected == lnum ) ? true : false ,
)
2021-05-26 15:11:35 +05:30
output_builder . push " #{ lnum } : #{ line } "
2023-01-09 12:10:19 +00:00
end
2021-05-26 15:11:35 +05:30
{ output : output_builder . join ( ) , array : hash_builder }
end
def raw
return @raw if defined? ( @raw )
m = @url . match ( self . raw_regexp )
if m
2023-01-09 12:10:19 +00:00
from = / \ d+ / . match ( m [ :from ] ) #get numeric should only match a positive interger
to = / \ d+ / . match ( m [ :to ] ) #get numeric should only match a positive interger
2021-05-26 15:11:35 +05:30
@file = m [ :file ]
@lang = Onebox :: FileTypeFinder . from_file_name ( m [ :file ] )
2023-01-20 12:52:49 -06:00
if @lang == " stl " && link . match? ( %r{ \ Ahttps?://(www \ .)?github \ .com.*/blob/ } )
2021-05-26 15:11:35 +05:30
@model_file = @lang . dup
@raw = " https://render.githubusercontent.com/view/solid?url= " + self . raw_template ( m )
else
2021-10-27 11:39:28 +03:00
contents = URI . parse ( self . raw_template ( m ) ) . open ( read_timeout : timeout ) . read
2021-05-26 15:11:35 +05:30
2022-01-20 23:47:18 +02:00
if contents . encoding == Encoding :: BINARY || contents . bytes . include? ( 0 )
2022-01-19 14:45:36 +02:00
@raw = nil
@binary = true
return
end
2023-01-09 12:10:19 +00:00
contents_lines = contents . lines #get contents lines
2021-05-26 15:11:35 +05:30
contents_lines_size = contents_lines . size #get number of lines
2023-01-09 12:10:19 +00:00
cr = calc_range ( m , contents_lines_size ) #calculate the range of lines for output
2021-05-26 15:11:35 +05:30
selected_one_liner = cr [ :selected_one_liner ] #if url is a one-liner calc_range will return it
from = cr [ :from ]
to = cr [ :to ]
@truncated = cr [ :truncated ]
range_provided = cr [ :range_provided ]
@cr_results = cr
2023-01-09 12:10:19 +00:00
if range_provided #if a range provided (single line or more)
2021-05-26 15:11:35 +05:30
if SHOW_LINE_NUMBER
2023-01-09 12:10:19 +00:00
lines_result =
line_number_helper (
contents_lines [ ( from - 1 ) .. ( to - 1 ) ] ,
from ,
selected_one_liner ,
) #print code with prefix line numbers in case range provided
2021-05-26 15:11:35 +05:30
contents = lines_result [ :output ]
@selected_lines_array = lines_result [ :array ]
else
contents = contents_lines [ ( from - 1 ) .. ( to - 1 ) ] . join ( )
end
else
contents = contents_lines [ ( from - 1 ) .. ( to - 1 ) ] . join ( )
end
2023-01-09 12:10:19 +00:00
if contents . length > MAX_CHARS #truncate content chars to limits
2021-05-26 15:11:35 +05:30
contents = contents [ 0 .. MAX_CHARS ]
@truncated = true
end
@raw = contents
end
end
end
def data
@data || = {
title : title ,
link : link ,
# IMPORTANT NOTE: All of the other class variables are populated
# as *side effects* of the `raw` method! They must all appear
# AFTER the call to `raw`! Don't get bitten by this like I did!
content : raw ,
2022-01-19 14:45:36 +02:00
binary : @binary ,
2021-05-26 15:11:35 +05:30
lang : " lang- #{ @lang } " ,
2023-01-09 12:10:19 +00:00
lines : @selected_lines_array ,
2021-05-26 15:11:35 +05:30
has_lines : ! @selected_lines_array . nil? ,
selected_one_liner : @selected_one_liner ,
cr_results : @cr_results ,
truncated : @truncated ,
model_file : @model_file ,
width : 480 ,
2023-01-09 12:10:19 +00:00
height : 360 ,
2021-05-26 15:11:35 +05:30
}
end
end
end
end
end